Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
11use brokk_bifrost_core::analyzer::common::{
12    parse_source_ranges_with_cancellation, parse_source_region,
13};
14pub use brokk_bifrost_core::analyzer::cpp_facts::{
15    CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
16    CppComparableSlot,
17};
18use brokk_bifrost_core::analyzer::fq_name::{
19    FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
20};
21use brokk_bifrost_core::analyzer::model::LanguageDialect;
22use brokk_bifrost_core::analyzer::model::{
23    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
24    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
25    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
26    ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
27    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
28};
29use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
30use brokk_bifrost_core::analyzer::source_facts::{
31    PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
32    SourceOccurrenceProvenance,
33};
34use brokk_bifrost_core::analyzer::statement_facts::CppStatementFactRows;
35use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
36use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
37use brokk_bifrost_core::analyzer::structural::materialization::{
38    GenerationKind, MaterializationRecord,
39};
40use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
41use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
42use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
43use brokk_bifrost_core::analyzer::tree_walk::{
44    NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
45    named_children_iter, push_children_reversed, push_named_children_reversed, subtree_contains,
46    walk_named_tree_preorder,
47};
48use brokk_bifrost_core::analyzer::usages::common::same_node;
49use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
50use brokk_bifrost_core::hash::{HashMap, HashSet};
51use regex::Regex;
52use std::cell::RefCell;
53use std::sync::Arc;
54use tree_sitter::{Node, Tree};
55
56/// Strip a tag keyword from the type spelling returned by C++ tooling.
57pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
58    let value = value.trim_start_matches("const ");
59    value
60        .strip_prefix("struct ")
61        .or_else(|| value.strip_prefix("class "))
62        .or_else(|| value.strip_prefix("enum "))
63        .unwrap_or(value)
64        .trim()
65}
66
67/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
68/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
69/// `::` head is exactly the mixed-separator store issue #1163 is about; the
70/// structured form records each namespace component, and the equivalence check
71/// renders it natively (with `::` between adjacent Package segments) so it
72/// round-trips to the legacy string. Splitting the already-joined string here is
73/// the M1 bridge — the legacy strings stay authoritative until M3.
74fn cpp_push_package(fq: &mut FqName, package_name: &str) {
75    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
76        fq.push(segment_interner().intern(component, SegmentKind::Package));
77    }
78}
79
80/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
81const CPP_PACKAGE_SEPARATOR: &str = "::";
82
83/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
84/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
85/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
86/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
87/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
88/// cpp-specific native rendering rule is needed for this chain.
89fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
90    let mut first = true;
91    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
92    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
93    // entry point, not re-inference of an already-structured name.
94    for component in chain.split('$').filter(|c| !c.is_empty()) {
95        let kind = if first {
96            SegmentKind::Type
97        } else {
98            SegmentKind::Nested
99        };
100        fq.push(segment_interner().intern(component, kind));
101        first = false;
102    }
103}
104
105/// Structured name for a C++ namespace module: every `::`-separated component is
106/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
107/// `short_name` with an empty `package_name`).
108fn cpp_namespace_fq(full_name: &str) -> FqName {
109    let mut fq = FqName::new();
110    cpp_push_package(&mut fq, full_name);
111    fq
112}
113
114/// The per-level namespace components a `namespace_definition`'s `name` field
115/// declares.
116///
117/// A C++17 nested definition (`namespace a::b::c`) parses as a
118/// `nested_namespace_specifier` whose named children are the per-level
119/// `namespace_identifier`s plus, for three or more levels, a further
120/// `nested_namespace_specifier`; the `::` separators, the optional per-level
121/// `inline`, and the leading global `::` are all anonymous tokens the walk
122/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
123/// segment path as the expanded `namespace a { namespace b { } }` form.
124///
125/// A shape outside that grammar is the deliberately ill-formed source the
126/// diagnostic corpora carry. Those keep their historical single-component
127/// reading of the raw name text, which the caller still joins to the lexical
128/// namespace exactly as before.
129fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
130    let mut components = Vec::new();
131    let mut stack = vec![node];
132    while let Some(current) = stack.pop() {
133        match current.kind() {
134            "namespace_identifier" | "identifier" => {
135                components.push(normalize_cpp_whitespace(node_text(current, source)));
136            }
137            "nested_namespace_specifier" => {
138                for index in (0..current.named_child_count()).rev() {
139                    stack.push(
140                        current
141                            .named_child(index)
142                            .expect("index below the node's own named child count"),
143                    );
144                }
145            }
146            _ => return cpp_raw_namespace_name_components(node, source),
147        }
148    }
149    if components.iter().any(String::is_empty) {
150        return cpp_raw_namespace_name_components(node, source);
151    }
152    components
153}
154
155/// [`cpp_namespace_name_components`] with each level's own name token range,
156/// for the one-Module-per-level declarations of the `namespace a::b`
157/// shorthand: the name a level records must come from the token that spells
158/// it (#4292), not from whichever construct the walk reached first.
159fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
160    let mut components = Vec::new();
161    let mut stack = vec![node];
162    while let Some(current) = stack.pop() {
163        match current.kind() {
164            "namespace_identifier" | "identifier" => {
165                components.push((
166                    normalize_cpp_whitespace(node_text(current, source)),
167                    cpp_declaration_range(current),
168                ));
169            }
170            "nested_namespace_specifier" => {
171                for index in (0..current.named_child_count()).rev() {
172                    stack.push(
173                        current
174                            .named_child(index)
175                            .expect("index below the node's own named child count"),
176                    );
177                }
178            }
179            _ => return Vec::new(),
180        }
181    }
182    if components.iter().any(|(name, _)| name.is_empty()) {
183        return Vec::new();
184    }
185    components
186}
187
188/// The historical reading of a namespace name node: its whole source text as
189/// one component, with a leading global `::` marker dropped so the caller's
190/// global-scope handling stays the AST boundary rather than a text prefix.
191///
192/// This is the recovery path for source outside the C++ grammar, so the text it
193/// returns can carry a separator that names nothing. react-native-windows
194/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
195/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
196/// trailing separator with no tail. The caller stores this component in
197/// `short_name` and derives the fq by splitting it back apart, so an empty tail
198/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
199/// here, at the one place the malformed text enters, rather than leaving each
200/// consumer to guard (#2353, a variant of #1878).
201fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
202    let start = node
203        .child(0)
204        .filter(|child| !child.is_named() && child.kind() == "::")
205        .map_or(node.start_byte(), |marker| marker.end_byte());
206    let text = normalize_cpp_whitespace(
207        source
208            .get(start..node.end_byte())
209            .expect("namespace name node covers one source range"),
210    );
211    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
212    if text.is_empty() {
213        return Vec::new();
214    }
215    vec![text]
216}
217
218/// The namespace definition ancestors of `node`, ordered from outermost to innermost.
219pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
220    let mut namespaces = Vec::new();
221    let mut current = node.parent();
222    while let Some(parent) = current {
223        if parent.kind() == "namespace_definition" {
224            namespaces.push(parent);
225        }
226        current = parent.parent();
227    }
228    namespaces.reverse();
229    namespaces
230}
231
232/// Return the named namespace path that structurally encloses `node`.
233///
234/// This intentionally follows namespace AST ancestors rather than inspecting
235/// source text. Anonymous namespaces are not representable in the legacy C++
236/// package field, so a path containing one fails closed.
237fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
238    let mut components = Vec::new();
239    for current in enclosing_cpp_namespace_nodes(node) {
240        let name_node = current.child_by_field_name("name")?;
241        let name = normalize_cpp_whitespace(node_text(name_node, source));
242        if name.is_empty() {
243            return None;
244        }
245        components.push(name);
246    }
247    if components.is_empty() {
248        return None;
249    }
250    // Same shared empty-component decision as `cpp_push_package`, which splits
251    // this string back apart: an enclosing namespace whose own name node is
252    // malformed contributes a component carrying its own separator (#2353).
253    Some(
254        normalize_joined(
255            &components.join(CPP_PACKAGE_SEPARATOR),
256            CPP_PACKAGE_SEPARATOR,
257        )
258        .into_owned(),
259    )
260}
261
262/// Nested-class `$` join for short names. An anonymous parent class (empty
263/// short_name) contributes no segment: the FqName bridge drops empty
264/// components, so a bare `parent$child` join would desync `short_name` from
265/// the fq and trip the package/short boundary assert in
266/// `CodeUnit::with_signature_and_fq` (#2140).
267fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
268    cpp_join_short_name(parent_short, name, '$')
269}
270
271/// Member `.` join for short names; same anonymous-parent guard as
272/// [`cpp_join_nested_short`] (#2140).
273fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
274    cpp_join_short_name(parent_short, name, '.')
275}
276
277fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
278    if parent_short.is_empty() {
279        name.to_string()
280    } else {
281        format!("{parent_short}{separator}{name}")
282    }
283}
284
285/// Structural fq for a leaf declaration: the parent unit's fq plus this
286/// declaration's own name as one segment (or the package segments plus the
287/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
288/// chain, so a literal `$` inside a source identifier (Cython template
289/// substitution points, gcc `$`-identifiers) survives instead of corrupting
290/// the chain and tripping the package/short boundary assert (#2140).
291fn cpp_leaf_fq(
292    package_name: &str,
293    parent: Option<&CodeUnit>,
294    name: &str,
295    kind_if_nested: SegmentKind,
296    kind_if_top: SegmentKind,
297) -> FqName {
298    if let Some(parent) = parent {
299        parent
300            .fq()
301            .clone()
302            .with_pushed(segment_interner().intern(name, kind_if_nested))
303    } else {
304        let mut fq = FqName::new();
305        cpp_push_package(&mut fq, package_name);
306        fq.push(segment_interner().intern(name, kind_if_top));
307        fq
308    }
309}
310
311/// Structured name for a member unit (function, field, enumerator). The
312/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
313/// the member has an owner, by `.member`; free functions and globals have no
314/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
315/// C++ member names never contain a literal `.`, so the single `.` (if any)
316/// separates the owner chain from the member.
317pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
318    let mut fq = FqName::new();
319    cpp_push_package(&mut fq, package_name);
320    match short_name.rsplit_once('.') {
321        Some((owner_chain, member)) => {
322            cpp_push_type_chain(&mut fq, owner_chain);
323            fq.push(segment_interner().intern(member, SegmentKind::Member));
324        }
325        None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
326    }
327    fq
328}
329
330/// Structured name for a rendered C++ qualified name supplied at the input
331/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
332///
333/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
334/// it hands back one component for a nested-class chain (`Outer$Inner`). An
335/// exact-name lookup then asks the store for owner `ns` with identifier
336/// `Outer$Inner`, while the declaration persists as a
337/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
338/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
339/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
340/// boundary identical to the persisted one. A `$` that is *not* a nesting join
341/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
342/// and stays reachable through the identifier fallback, which seeks the whole
343/// component text as one spelling.
344pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
345    let mut fq = FqName::new();
346    for component in parse_symbol_path(Language::Cpp, rendered) {
347        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
348        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
349        // construction boundary `cpp_push_type_chain` owns for extraction.
350        let mut first = true;
351        for part in component.split('$').filter(|part| !part.is_empty()) {
352            let kind = if first {
353                // A client-typed head claims no kind, exactly as
354                // `parse_symbol_path_fq` records it.
355                SegmentKind::Unknown
356            } else {
357                SegmentKind::Nested
358            };
359            fq.push(segment_interner().intern(part, kind));
360            first = false;
361        }
362    }
363    fq
364}
365
366/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
367///
368/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
369/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
370/// `Top.Only`, and the exact-name retain drops the persisted nested identity
371/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
372/// placements extraction stores, so each exact query's parent and terminal
373/// match a persisted row and the structural retain keeps it.
374///
375/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
376/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
377/// is one of those: the `::` there is the package join the canonical name
378/// already spells.
379pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
380    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
381        return Vec::new();
382    }
383    let texts = parse_symbol_path(Language::Cpp, rendered);
384    if texts.len() < 2 {
385        return Vec::new();
386    }
387    cpp_qualified_kind_interpretations(&texts)
388}
389
390/// Every namespace / type / member boundary a `::`-joined source spelling can
391/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
392/// segments.
393fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
394    let len = texts.len();
395    let mut names = Vec::new();
396    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
397    for packages in 0..len {
398        let rest = len - packages;
399        let mut type_chain = Vec::with_capacity(len);
400        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
401        type_chain.push(SegmentKind::Type);
402        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
403        names.push(cpp_fq_with_kinds(texts, type_chain));
404        if rest >= 2 {
405            let mut member = Vec::with_capacity(len);
406            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
407            member.push(SegmentKind::Type);
408            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
409            member.push(SegmentKind::Member);
410            names.push(cpp_fq_with_kinds(texts, member));
411        } else if packages >= 1 {
412            let mut function = vec![SegmentKind::Package; packages];
413            function.push(SegmentKind::Member);
414            names.push(cpp_fq_with_kinds(texts, function));
415        }
416    }
417    names
418}
419
420fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
421    assert_eq!(
422        texts.len(),
423        kinds.len(),
424        "a qualified interpretation names every segment"
425    );
426    let mut fq = FqName::new();
427    for (text, kind) in texts.iter().zip(kinds) {
428        fq.push(segment_interner().intern(text, kind));
429    }
430    fq
431}
432
433#[derive(Clone)]
434pub struct ScopeInfo {
435    package_name: String,
436    module: Option<CodeUnit>,
437    class_unit: Option<CodeUnit>,
438    template_signature: Option<String>,
439    template_metadata: Option<CppTemplateMetadata>,
440    declarations_are_fields: bool,
441    recovered_specialization_member_scope: bool,
442    /// Namespace targets of every `using namespace X;` directive lexically
443    /// visible at this point in the file (declaration order), threaded
444    /// forward sibling-by-sibling by the sequential container walk (see
445    /// `CppWork::Siblings`). An out-of-line member definition written as a
446    /// bare `Class::method` at file/namespace scope with no enclosing
447    /// `namespace {}` block (issue #1093, e.g. log4cxx's
448    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
449    /// const { ... }`) has no other structural signal for which namespace
450    /// actually owns `Class`; this is the best-effort candidate list used to
451    /// recover it so the definition's indexed identity matches its header
452    /// declaration's.
453    visible_using_namespaces: Vec<String>,
454    /// Every namespace-scope `using ns::Type;` declaration lexically visible
455    /// at this point, threaded forward like `visible_using_namespaces`. Each
456    /// scope clones this list for every sibling it visits while only a using
457    /// declaration appends to it, so the list is shared copy-on-write.
458    visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
459}
460
461/// One `using ns::Type;` declaration at namespace scope: the name it
462/// introduces and the canonical namespace of the entity it names.
463///
464/// An out-of-line member definition whose owner qualifier starts with that
465/// name (`void Type::method() {}`) defines a member of `ns::Type`; the local
466/// spelling never creates a second owner (#6902 item 4).
467#[derive(Clone, Debug, PartialEq, Eq)]
468pub struct CppUsingDeclarationBinding {
469    /// The `::`-joined package of the namespace scope that holds the using
470    /// declaration. A binding proves an owner only for definitions in this
471    /// same scope: an inner namespace may declare its own class with the same
472    /// name, which per-file extraction cannot rule out.
473    scope_package: String,
474    /// The introduced name, the target's terminal component.
475    name: String,
476    /// The `::`-joined namespace of the target.
477    target_package: String,
478}
479
480struct CppContainer<'tree> {
481    node: Node<'tree>,
482    scope: ScopeInfo,
483}
484
485struct CppNodeWork<'tree> {
486    node: Node<'tree>,
487    scope: ScopeInfo,
488}
489
490/// Cursor over one container's remaining named children, processed one at a
491/// time (rather than all at once) so a `using namespace X;` sibling can
492/// update `scope.visible_using_namespaces` for the siblings that follow it,
493/// matching real C++ using-directive semantics. Nested container work is
494/// still pushed and fully drained before the cursor resumes (stack LIFO
495/// order), preserving the original left-to-right visitation order.
496struct CppSiblingsWork<'tree> {
497    children: std::vec::IntoIter<Node<'tree>>,
498    scope: ScopeInfo,
499}
500
501enum CppWork<'tree> {
502    Container(CppContainer<'tree>),
503    Node(CppNodeWork<'tree>),
504    Siblings(CppSiblingsWork<'tree>),
505}
506
507fn class_like_name<'tree>(
508    node: Node<'tree>,
509    source: &str,
510    ancestry: &ParentIndex<'tree>,
511) -> Option<String> {
512    let best = class_like_name_from_children(node, source);
513    if let Some(parent) = ancestry.parent(node)
514        && matches!(
515            parent.kind(),
516            "declaration" | "field_declaration" | "function_definition"
517        )
518        // A class_specifier carrying its own body proves the grammar name is
519        // the real class name: a sibling declarator then declares an object
520        // (`class X {} x;`), never a displaced class name. The gate matters
521        // when the class name is itself an all-caps token (`X`, `API`) --
522        // without it the export-macro re-read below steals the object
523        // declarator's name for the class (#2283). The genuine export-macro
524        // shapes leave the class_specifier bodyless, the same invariant
525        // recover_malformed_exported_multiple_base_class already gates on.
526        && cpp_body_node(node).is_none()
527        && node
528            .child_by_field_name("name")
529            .map(|name_node| {
530                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
531            })
532            .unwrap_or(false)
533        && let Some(recovered) = exported_class_name_from_node(parent, source)
534        && best.as_deref() != Some(recovered.as_str())
535    {
536        return Some(recovered);
537    }
538    best.or_else(|| {
539        node.child_by_field_name("name")
540            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
541            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
542    })
543}
544
545fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
546    let mut grammar_name = None;
547    if let Some(name_node) = node.child_by_field_name("name") {
548        let name = normalize_cpp_whitespace(node_text(name_node, source));
549        if name.is_empty() {
550            return None;
551        }
552        if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
553            return Some(name);
554        }
555        grammar_name = Some(name);
556    }
557
558    let mut best = None;
559    let mut cursor = node.walk();
560    let mut stack = Vec::new();
561    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
562        if matches!(
563            child.kind(),
564            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
565        ) {
566            break;
567        }
568        stack.push(child);
569    }
570
571    while let Some(current) = stack.pop() {
572        // Native attributes decorate the class; their identifiers cannot name it.
573        if current.kind() == "attribute_declaration" {
574            continue;
575        }
576        if matches!(current.kind(), "type_identifier" | "identifier") {
577            let name = normalize_cpp_whitespace(node_text(current, source));
578            if !name.is_empty() && !cpp_export_macro_token(&name) {
579                best = Some(name);
580            }
581            continue;
582        }
583
584        push_named_children_reversed(current, &mut stack);
585    }
586    best.or(grammar_name)
587}
588
589pub fn cpp_export_macro_token(token: &str) -> bool {
590    token
591        .chars()
592        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
593}
594
595struct RecoveredExportedClass<'tree> {
596    declaration_node: Node<'tree>,
597    name: String,
598    body: Option<Node<'tree>>,
599    raw_supertypes: Option<Vec<String>>,
600    base_facts: Option<Vec<CppBaseSpecifierFact>>,
601    uses_initializer_body: bool,
602    /// Present only for the fragmented multiple-base export shape (issue #938).
603    /// Carries the true class-body byte region -- the members tree-sitter scattered
604    /// out of the recovered node -- so they can be reparsed and re-owned as members
605    /// rather than lost inside the truncated `initializer_list` stand-in.
606    fragmented_body: Option<FragmentedExportBody>,
607}
608
609struct RecoveredFunctionLikeExportClassPair {
610    name: String,
611    range: Range,
612    raw_supertypes: Option<Vec<String>>,
613    base_facts: Option<Vec<CppBaseSpecifierFact>>,
614    fragmented_body: FragmentedExportBody,
615}
616
617struct RecoveredEmbeddedFunctionLikeExportClass {
618    name: String,
619    range: Range,
620    raw_supertypes: Vec<String>,
621    base_facts: Vec<CppBaseSpecifierFact>,
622    fragmented_body: FragmentedExportBody,
623}
624
625struct RecoveredFunctionLikeExportClass {
626    name: String,
627    range: Range,
628    raw_supertypes: Option<Vec<String>>,
629    base_facts: Option<Vec<CppBaseSpecifierFact>>,
630    member_outcome: Option<FragmentedExportMembers>,
631}
632
633/// The recovered class-body geometry for a fragmented multiple-base export class.
634/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
635/// verbatim for a region reparse (issue #941 machinery) so every recovered member
636/// keeps its exact original byte/line position. `class_range` is the full class
637/// navigation range spanning to the displaced closing brace.
638struct FragmentedExportBody {
639    reparse_start: usize,
640    reparse_end: usize,
641    class_range: Range,
642}
643
644fn recovered_fragmented_export_body(
645    body: Node<'_>,
646    class_range: Range,
647) -> Option<FragmentedExportBody> {
648    let open = body.child(0).filter(|child| child.kind() == "{")?;
649    let close = body
650        .child(body.child_count().saturating_sub(1))
651        .filter(|child| child.kind() == "}" && !child.is_missing());
652    Some(FragmentedExportBody {
653        reparse_start: open.end_byte(),
654        // A zero-width missing `}` contributes no source byte. Keep the whole
655        // body range in that case; subtracting one byte would discard the last
656        // member's semicolon and make the otherwise valid region unsafe to
657        // index. A real close token is excluded by its structured start.
658        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
659        class_range,
660    })
661}
662
663struct DisplacedFragmentNamespaceBoundary<'tree> {
664    class_close: Node<'tree>,
665    class_semicolon: Node<'tree>,
666    namespace_items: Vec<Node<'tree>>,
667}
668
669/// Result of validating a reparsed fragmented class body.  A complete tree can
670/// safely consume the whole region.  A partial tree may contain only the exact
671/// class-named constructor that tree-sitter merged into an access label; its
672/// remaining siblings must stay on the ordinary outer walk.
673enum FragmentedExportMembers {
674    Complete(Tree),
675    ConditionalConstructor(Tree),
676}
677
678#[derive(Clone, Copy)]
679struct DisplacedMacroClassTail {
680    split_index: usize,
681    class_range: Range,
682}
683
684fn recover_exported_class_declaration<'tree>(
685    node: Node<'tree>,
686    source: &str,
687) -> Option<RecoveredExportedClass<'tree>> {
688    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
689        return Some(recovered);
690    }
691
692    let class_node = first_class_like_child(node)?;
693    if let Some(name_node) = class_node.child_by_field_name("name") {
694        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
695        if cpp_export_macro_token(&class_name) {
696            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
697            // Name declarator. Only a bare declarator can be the displaced class name;
698            // wrappers describe an object whose type merely happens to look macro-like.
699            let mut cursor = node.walk();
700            if node
701                .children_by_field_name("declarator", &mut cursor)
702                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
703            {
704                return None;
705            }
706        } else if has_direct_cpp_declarator(node) {
707            return None;
708        }
709    }
710    let name = exported_class_name_from_node(class_node, source)?;
711    Some(RecoveredExportedClass {
712        declaration_node: class_node,
713        name,
714        body: cpp_body_node(class_node),
715        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
716            .then(|| extract_cpp_supertypes(class_node, source)),
717        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
718            .then(|| extract_cpp_base_facts(class_node, source)),
719        uses_initializer_body: false,
720        fragmented_body: None,
721    })
722}
723
724fn recover_malformed_exported_base_class<'tree>(
725    node: Node<'tree>,
726    source: &str,
727) -> Option<RecoveredExportedClass<'tree>> {
728    if node.kind() != "declaration" {
729        return None;
730    }
731    let class_node = node.child_by_field_name("type")?;
732    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
733        return None;
734    }
735    let macro_name = class_node
736        .child_by_field_name("name")
737        .and_then(|name| direct_identifier_name(name, source))?;
738    if !cpp_export_macro_token(&macro_name) {
739        return None;
740    }
741
742    let mut named_cursor = node.walk();
743    let mut named = node.named_children(&mut named_cursor);
744    // Object-like macro lines before the head are provenance modifiers under
745    // the macro-declaration grammar; the class node follows them.
746    let head_matches_class = loop {
747        let child = named.next()?;
748        if child.kind() == "macro_modifier" {
749            continue;
750        }
751        break same_node(child, class_node);
752    };
753    if !head_matches_class {
754        return None;
755    }
756    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
757    if displaced.kind() == "init_declarator" {
758        // With a single base and an empty body the entire malformed head is
759        // the initializer's declarator, rather than a sibling of it.
760        if named.next().is_some() {
761            return None;
762        }
763        let declarator = displaced.child_by_field_name("declarator")?;
764        let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
765        let body = displaced.child_by_field_name("value")?;
766        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
767            return None;
768        }
769        return Some(RecoveredExportedClass {
770            declaration_node: node,
771            name,
772            body: Some(body),
773            raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
774            base_facts: Some(vec![recovered_base_specifier(
775                base,
776                source,
777                recovered_base_is_virtual(declarator, base, source),
778            )?]),
779            uses_initializer_body: true,
780            fragmented_body: fragmented_export_body_region(node, body, source),
781        });
782    }
783    if displaced.kind() == "parenthesized_declarator" {
784        // The macro-declaration grammar keeps the invocation, the displaced
785        // class name and the first base inside a `parenthesized_declarator`,
786        // and the remaining bases beside the body in the trailing
787        // `init_declarator`.
788        let (name, first_base_node) =
789            recovered_function_like_export_class_owner_node(displaced, source)?;
790        let remaining = named.collect::<Vec<_>>();
791        let [init] = remaining.as_slice() else {
792            return None;
793        };
794        if init.kind() != "init_declarator" {
795            return None;
796        }
797        let mut final_base_node = init.child_by_field_name("declarator")?;
798        while final_base_node.kind() == "macro_modifier_declarator" {
799            final_base_node = final_base_node.child_by_field_name("declarator")?;
800        }
801        let first_base = recovered_malformed_base_name(first_base_node, source)?;
802        let final_base = recovered_malformed_base_name(final_base_node, source)?;
803        let body = init.child_by_field_name("value")?;
804        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
805            return None;
806        }
807        return Some(RecoveredExportedClass {
808            declaration_node: node,
809            name,
810            body: Some(body),
811            raw_supertypes: Some(vec![first_base, final_base]),
812            base_facts: Some(vec![
813                recovered_base_specifier(
814                    first_base_node,
815                    source,
816                    recovered_base_is_virtual(displaced, first_base_node, source),
817                )?,
818                recovered_base_specifier(
819                    final_base_node,
820                    source,
821                    recovered_base_is_virtual(*init, final_base_node, source),
822                )?,
823            ]),
824            uses_initializer_body: true,
825            fragmented_body: fragmented_export_body_region(node, body, source),
826        });
827    }
828    if displaced.kind() != "ERROR" {
829        return None;
830    }
831    let name = displaced_exported_class_name(displaced, source)?;
832
833    let remaining = named.collect::<Vec<_>>();
834    let init = *remaining.last()?;
835    if init.kind() != "init_declarator" {
836        return None;
837    }
838    let final_base = init
839        .child_by_field_name("declarator")
840        .and_then(|base| recovered_malformed_base_name(base, source))?;
841    let body = init.child_by_field_name("value")?;
842    // A complete reduction has a real closing brace here. In Chromium's Widget
843    // declaration, tree-sitter instead emits the same direct `}` slot as a
844    // zero-width missing node where the first body macro truncates the prefix.
845    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
846        return None;
847    }
848
849    if remaining[..remaining.len() - 1]
850        .iter()
851        .any(|child| match child.kind() {
852            "qualified_identifier"
853            | "scoped_type_identifier"
854            | "type_identifier"
855            | "identifier"
856            | "macro_modifier_declarator" => false,
857            "ERROR" => !is_malformed_inheritance_access(*child, source),
858            _ => true,
859        })
860    {
861        return None;
862    }
863
864    let mut raw_supertypes = Vec::new();
865    let mut base_facts = Vec::new();
866    for base in &remaining[..remaining.len() - 1] {
867        if base.kind() == "ERROR" {
868            continue;
869        }
870        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
871        base_facts.push(recovered_base_specifier(
872            *base,
873            source,
874            recovered_base_is_virtual(node, *base, source),
875        )?);
876    }
877    raw_supertypes.push(final_base);
878    let final_base_node = init.child_by_field_name("declarator")?;
879    base_facts.push(recovered_base_specifier(
880        final_base_node,
881        source,
882        recovered_base_is_virtual(init, final_base_node, source),
883    )?);
884
885    Some(RecoveredExportedClass {
886        declaration_node: node,
887        name,
888        body: Some(body),
889        raw_supertypes: Some(raw_supertypes),
890        base_facts: Some(base_facts),
891        uses_initializer_body: true,
892        fragmented_body: fragmented_export_body_region(node, body, source),
893    })
894}
895
896/// Locate the true class-body region for a fragmented multiple-base export class.
897///
898/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
899/// emits in place of the real class body. Tree-sitter reduces that body in one of
900/// two shapes, both of which lose the members from the recovered node:
901///
902/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
903///   a real closing brace and holds the whole body text inline. The interior between
904///   the braces reparses to the members directly.
905/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
906///   first member with a zero-width MISSING `}`; every later member -- and the real
907///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
908///   siblings. The interior runs from the opening brace to that displaced `}`.
909///
910/// Returns the interior byte range to reparse plus the full class navigation range.
911fn fragmented_export_body_region(
912    node: Node<'_>,
913    body: Node<'_>,
914    source: &str,
915) -> Option<FragmentedExportBody> {
916    let reparse_start = body.start_byte() + 1;
917    let close = direct_close_brace(body)?;
918    if close.end_byte() > close.start_byte() {
919        return Some(FragmentedExportBody {
920            reparse_start,
921            reparse_end: close.start_byte(),
922            class_range: cpp_declaration_range(node),
923        });
924    }
925    // The closing brace was displaced past the recovered node. A balanced nested
926    // class keeps its own braces, so the first lone-`}` sibling is this class's.
927    let mut sibling = node.next_named_sibling();
928    let displaced_close = loop {
929        let Some(current) = sibling else {
930            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
931        };
932        if cpp_is_stray_close_brace(current, source) {
933            break current;
934        }
935        sibling = current.next_named_sibling();
936    };
937    Some(FragmentedExportBody {
938        reparse_start,
939        reparse_end: displaced_close.start_byte(),
940        class_range: Range {
941            start_byte: node.start_byte(),
942            end_byte: displaced_close.end_byte(),
943            start_line: node.start_position().row + 1,
944            end_line: displaced_close.end_position().row + 1,
945        },
946    })
947}
948
949/// Locate the true class-body region for the export-macro class shape that
950/// tree-sitter promotes to a `function_definition`.
951///
952/// In this shape the synthetic function body closes at the first inline
953/// method, while the class's real members continue as root-level siblings until
954/// a stray `}` followed by the displaced class `;`. Reparse the complete
955/// interior so those siblings are visited with the recovered class scope.
956fn fragmented_export_function_body_region(
957    node: Node<'_>,
958    body: Node<'_>,
959    source: &str,
960    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
961) -> Option<FragmentedExportBody> {
962    let reparse_start = body.start_byte().checked_add(1)?;
963    if let Some(boundary) = displaced_namespace {
964        return Some(FragmentedExportBody {
965            reparse_start,
966            reparse_end: boundary.class_close.start_byte(),
967            class_range: Range {
968                start_byte: node.start_byte(),
969                end_byte: boundary.class_semicolon.end_byte(),
970                start_line: node.start_position().row + 1,
971                end_line: boundary.class_semicolon.end_position().row + 1,
972            },
973        });
974    }
975    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
976    let boundary =
977        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
978    let boundary_index = boundary.and_then(|boundary| {
979        siblings
980            .iter()
981            .position(|candidate| same_node(*candidate, boundary))
982    });
983    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
984    let mut sibling_index = 0;
985    // A complete recovered class's synthetic wrapper is immediately followed
986    // by its displaced semicolon (comments and a trailing attribute macro --
987    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
988    // may sit between the body and that semicolon). Only scan for a later
989    // stray close when real member siblings intervene; otherwise every earlier
990    // complete class would borrow the next malformed class's close and claim
991    // its members. The trailing-attribute case is the gtest shape: the scan
992    // borrowed a close ~1900 lines later and re-owned a following
993    // `namespace testing { namespace internal {` block as class members,
994    // doubling the package path ("testing::internal::testing::internal") and
995    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
996    // boundary assert (#2297).
997    while let Some(current) = siblings.get(sibling_index).copied() {
998        if current.kind() == "comment" {
999            sibling_index += 1;
1000            continue;
1001        }
1002        if is_trailing_attribute_macro_sibling(current) {
1003            sibling_index += 1;
1004            continue;
1005        }
1006        if cpp_is_stray_semicolon(current, source) {
1007            return None;
1008        }
1009        break;
1010    }
1011    while let Some(current) = siblings.get(sibling_index).copied() {
1012        let next = siblings.get(sibling_index + 1).copied();
1013        if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1014            return Some(FragmentedExportBody {
1015                reparse_start,
1016                reparse_end: current.start_byte(),
1017                class_range: Range {
1018                    start_byte: node.start_byte(),
1019                    end_byte: semicolon.end_byte(),
1020                    start_line: node.start_position().row + 1,
1021                    end_line: semicolon.end_position().row + 1,
1022                },
1023            });
1024        }
1025        // When the final access label keeps the class close in its malformed
1026        // declaration body, tree-sitter nests the lone `}` ERROR below the
1027        // label instead of exposing it as a direct sibling. Search only the
1028        // scattered siblings after the synthetic wrapper. The first such
1029        // close is the class terminator because nested class bodies retain
1030        // their own balanced class_specifier nodes.
1031        if current.start_byte() >= body.end_byte()
1032            && let Some(close) = cpp_nested_stray_close_brace(current, source)
1033        {
1034            return Some(FragmentedExportBody {
1035                reparse_start,
1036                reparse_end: close.start_byte(),
1037                class_range: Range {
1038                    start_byte: node.start_byte(),
1039                    end_byte: current.end_byte(),
1040                    start_line: node.start_position().row + 1,
1041                    end_line: current.end_position().row + 1,
1042                },
1043            });
1044        }
1045        sibling_index += 1;
1046    }
1047    boundary.map(|boundary| FragmentedExportBody {
1048        reparse_start,
1049        reparse_end: boundary.start_byte(),
1050        class_range: Range {
1051            start_byte: node.start_byte(),
1052            end_byte: boundary.start_byte(),
1053            start_line: node.start_position().row + 1,
1054            end_line: boundary.start_position().row + 1,
1055        },
1056    })
1057}
1058
1059/// Find a later macro-export class that tree-sitter lifted through an enclosing
1060/// preprocessor container. A class that is still a direct sibling can be a
1061/// nested member of the current fragmented class, so only a changed parent is
1062/// a proven boundary between the two recovered class envelopes.
1063fn fragmented_export_sibling_class_boundary<'tree>(
1064    node: Node<'tree>,
1065    source: &str,
1066    ancestry: &ParentIndex<'tree>,
1067) -> Option<Node<'tree>> {
1068    let node_parent = ancestry.parent(node)?;
1069    cpp_following_named_siblings(node, source, ancestry)
1070        .into_iter()
1071        .find(|candidate| {
1072            recover_exported_class_function_definition(*candidate, source).is_some()
1073                && ancestry
1074                    .parent(*candidate)
1075                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1076        })
1077}
1078
1079/// A trailing attribute macro after a recovered class's closing brace, spelled
1080/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
1081/// bare identifier is never a class member (members need a type), so this
1082/// sibling can only be the class's own tail (#2297).
1083fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1084    if node.kind() != "expression_statement" {
1085        return false;
1086    }
1087    let mut cursor = node.walk();
1088    let mut children = node.named_children(&mut cursor);
1089    children
1090        .next()
1091        .is_some_and(|child| child.kind() == "identifier")
1092        && children.next().is_none()
1093}
1094
1095/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
1096/// export-class wrapper can place the class close inside an access-label node,
1097/// so direct-sibling checks alone miss the boundary.  Walk named CST children
1098/// only; the helper does not inspect source text beyond the existing structured
1099/// stray-brace predicate.
1100fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1101    let mut stack = vec![node];
1102    while let Some(current) = stack.pop() {
1103        if cpp_is_stray_close_brace(current, source) {
1104            return Some(current);
1105        }
1106        let mut cursor = current.walk();
1107        stack.extend(current.named_children(&mut cursor));
1108    }
1109    None
1110}
1111
1112/// Return named siblings that follow `node`, including siblings that tree-sitter
1113/// attached to an enclosing container after malformed recovery split the local
1114/// declaration list. Stop at the first structurally visible class close so a
1115/// later namespace or exported class cannot supply the recovery boundary.
1116///
1117/// The climb takes a [`ParentIndex`] because it runs once per visited node and
1118/// walks every level to the root: with `Node::parent`, which re-descends from
1119/// the root on each call, a translation unit holding thousands of siblings --
1120/// generated instruction tables, for instance -- made this the whole cost of
1121/// indexing the file. Callers inside the declaration walk pass the walk's
1122/// index; the four that ask from outside a traversal pass
1123/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
1124/// behaviour is unchanged.
1125fn cpp_following_named_siblings<'tree>(
1126    node: Node<'tree>,
1127    source: &str,
1128    ancestry: &ParentIndex<'tree>,
1129) -> Vec<Node<'tree>> {
1130    let mut siblings = Vec::new();
1131    let mut anchor = node;
1132    while let Some(parent) = ancestry.parent(anchor) {
1133        let at_translation_unit = parent.kind() == "translation_unit";
1134        let mut cursor = parent.walk();
1135        assert!(
1136            cursor.goto_first_child(),
1137            "ParentIndex returned a parent without children"
1138        );
1139        while !same_node(cursor.node(), anchor) {
1140            assert!(
1141                cursor.goto_next_sibling(),
1142                "ParentIndex parent does not contain its child anchor"
1143            );
1144        }
1145        while cursor.goto_next_sibling() {
1146            let current = cursor.node();
1147            if !current.is_named() {
1148                continue;
1149            }
1150            if at_translation_unit
1151                && (current.kind() == "namespace_definition"
1152                    || (current.kind() == "function_definition"
1153                        && first_class_like_child(current).is_some()))
1154            {
1155                return siblings;
1156            }
1157            siblings.push(current);
1158            if cpp_is_stray_close_brace(current, source) {
1159                if let Some(semicolon) = current
1160                    .next_named_sibling()
1161                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1162                {
1163                    siblings.push(semicolon);
1164                }
1165                return siblings;
1166            }
1167            if current.start_byte() >= node.end_byte()
1168                && matches!(current.kind(), "ERROR" | "labeled_statement")
1169                && cpp_nested_stray_close_brace(current, source).is_some()
1170            {
1171                return siblings;
1172            }
1173        }
1174        anchor = parent;
1175    }
1176    siblings
1177}
1178
1179fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1180    if node.start_byte() >= class_end {
1181        return false;
1182    }
1183    node.end_byte() <= class_end
1184        || cpp_nested_stray_close_brace(node, source)
1185            .is_some_and(|close| close.start_byte() == class_end)
1186}
1187
1188/// A class whose head tree-sitter kept in one malformed container while the
1189/// members after the first and the closing `};` scattered across that
1190/// container's following siblings.
1191struct FragmentedClassRecovery<'tree> {
1192    declaration_node: Node<'tree>,
1193    name: String,
1194    raw_supertypes: Vec<String>,
1195    body: FragmentedExportBody,
1196}
1197
1198/// Recover a class whose opening prefix is retained in one ERROR node while one
1199/// or more nested class closes and the outer close are displaced to sibling
1200/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1201/// tokens establish nesting depth and the displaced close nodes terminate it.
1202///
1203/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1204/// `class` keyword as the container's first token. A function-like export macro
1205/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1206/// body-less `class_specifier` named after the macro followed by the
1207/// invocation's `(`, arguments and `)`, with the real class name and the base
1208/// clause left as bare tokens after it (#2924).
1209fn fragmented_class_body<'tree>(
1210    node: Node<'tree>,
1211    source: &str,
1212) -> Option<FragmentedClassRecovery<'tree>> {
1213    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1214        return Some(recovered);
1215    }
1216    let supported_container = node.kind() == "ERROR"
1217        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1218    if !supported_container {
1219        return None;
1220    }
1221    let mut cursor = node.walk();
1222    let children = node.children(&mut cursor).collect::<Vec<_>>();
1223    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1224        return Some(recovered);
1225    }
1226    let keyword = children.first()?;
1227    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1228        return None;
1229    }
1230    let name_node = children
1231        .iter()
1232        .copied()
1233        .skip(1)
1234        .find(|child| child.is_named())?;
1235    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1236        return None;
1237    }
1238    let name = normalize_cpp_whitespace(node_text(name_node, source));
1239    if name.is_empty() || cpp_export_macro_token(&name) {
1240        return None;
1241    }
1242    let open_index = children.iter().position(|child| child.kind() == "{")?;
1243    let open = children[open_index];
1244    let body = displaced_fragment_namespace_geometry(node, source)
1245        .map(|boundary| FragmentedExportBody {
1246            reparse_start: open.end_byte(),
1247            reparse_end: boundary.class_close.start_byte(),
1248            class_range: Range {
1249                start_byte: node.start_byte(),
1250                end_byte: boundary.class_semicolon.end_byte(),
1251                start_line: node.start_position().row + 1,
1252                end_line: boundary.class_semicolon.end_position().row + 1,
1253            },
1254        })
1255        .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1256    Some(FragmentedClassRecovery {
1257        declaration_node: node,
1258        name,
1259        raw_supertypes: extract_cpp_supertypes(node, source),
1260        body,
1261    })
1262}
1263
1264/// The fragmented body of a function-like export-macro class head that
1265/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1266/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1267/// public virtual EC_PublicKey {`, #2924).
1268///
1269/// The class name is read positionally by [`recovered_export_head_name`] -- the
1270/// last identifier before the head ends at `final`, at the base clause `:`, or
1271/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1272/// spelled with `virtual`, with `final`, or with qualified base names needs no
1273/// arm of its own.
1274fn fragmented_export_macro_class_body<'tree>(
1275    node: Node<'tree>,
1276    children: &[Node<'tree>],
1277    source: &str,
1278) -> Option<FragmentedClassRecovery<'tree>> {
1279    let class_node = *children.first()?;
1280    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1281        return None;
1282    }
1283    // The identifier tree-sitter took for the class name is the export macro, a
1284    // function-like invocation when the `(` of its argument list follows it
1285    // directly. Spelling plays no part (#2557).
1286    class_node
1287        .child_by_field_name("name")
1288        .and_then(|name| direct_identifier_name(name, source))?;
1289    let invocation = *children.get(1)?;
1290    if invocation.is_named() || invocation.kind() != "(" {
1291        return None;
1292    }
1293    let open_index = children
1294        .iter()
1295        .position(|child| !child.is_named() && child.kind() == "{")?;
1296    let open = children[open_index];
1297    let name_node = recovered_export_head_name(node, open, source)?;
1298    let name = normalize_cpp_whitespace(node_text(name_node, source));
1299    if name.is_empty() {
1300        return None;
1301    }
1302    Some(FragmentedClassRecovery {
1303        declaration_node: node,
1304        name,
1305        raw_supertypes: recovered_export_head_bases(
1306            node,
1307            name_node.end_byte(),
1308            open.start_byte(),
1309            source,
1310        ),
1311        body: fragmented_displaced_class_body(node, children, open_index, source)
1312            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1313    })
1314}
1315
1316/// The class body a fragmented head opened at `children[open_index]` but could
1317/// not close: tree-sitter scattered the remaining members and the closing `};`
1318/// across the container's following siblings. The direct class tokens after the
1319/// opening brace say how many closes the nested classes consume before this
1320/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1321fn fragmented_displaced_class_body(
1322    node: Node<'_>,
1323    children: &[Node<'_>],
1324    open_index: usize,
1325    source: &str,
1326) -> Option<FragmentedExportBody> {
1327    let open = children[open_index];
1328    let nested_class_opens = children[open_index + 1..]
1329        .iter()
1330        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1331        .count();
1332    let mut closes_remaining = 1 + nested_class_opens;
1333    let mut sibling = node.next_named_sibling();
1334    while let Some(candidate) = sibling {
1335        let next = candidate.next_named_sibling();
1336        // The class's own close: a lone `}` `ERROR` the mis-parse split off,
1337        // or -- under the macro-declaration grammar, which keeps the fragmented
1338        // head in an `ERROR` and the close as a plain child of the enclosing
1339        // list -- a bare `}` there. The `;` after it is what proves the close
1340        // is the class's own rather than the enclosing scope's.
1341        let is_close = cpp_is_stray_close_brace(candidate, source)
1342            || (candidate.kind() == "}" && !candidate.is_named());
1343        if is_close {
1344            closes_remaining -= 1;
1345            if closes_remaining == 0 {
1346                let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1347                if open.end_byte() >= candidate.start_byte() {
1348                    return None;
1349                }
1350                return Some(FragmentedExportBody {
1351                    reparse_start: open.end_byte(),
1352                    reparse_end: candidate.start_byte(),
1353                    class_range: Range {
1354                        start_byte: node.start_byte(),
1355                        end_byte: semicolon.end_byte(),
1356                        start_line: node.start_position().row + 1,
1357                        end_line: semicolon.end_position().row + 1,
1358                    },
1359                });
1360            }
1361        }
1362        sibling = next;
1363    }
1364    None
1365}
1366
1367/// The body of a fragmented export-macro class head whose close tree-sitter
1368/// spent on the enclosing container instead: Botan's
1369/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1370/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1371/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1372/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1373/// (#2924).
1374fn fragmented_container_close_class_body(
1375    node: Node<'_>,
1376    open: Node<'_>,
1377    source: &str,
1378) -> Option<FragmentedExportBody> {
1379    let parent = node.parent()?;
1380    if !matches!(
1381        parent.kind(),
1382        "declaration_list" | "field_declaration_list" | "compound_statement"
1383    ) {
1384        return None;
1385    }
1386    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1387    // The container's close is this class's close only when the body's own
1388    // brace balance says so. Without that cross-check -- the one issue #1524
1389    // already uses on a mis-closed wrapper body -- a class that really does end
1390    // earlier would claim every later container-level declaration as a member.
1391    if OrphanedNamespaceScopeIndex::build(parent, source)
1392        .matching_close_brace(open.start_byte())
1393        .map(|close| close.start_byte)
1394        != Some(close.start_byte())
1395        || open.end_byte() >= close.start_byte()
1396    {
1397        return None;
1398    }
1399    Some(FragmentedExportBody {
1400        reparse_start: open.end_byte(),
1401        reparse_end: close.start_byte(),
1402        class_range: Range {
1403            start_byte: node.start_byte(),
1404            end_byte: close.end_byte(),
1405            start_line: node.start_position().row + 1,
1406            end_line: close.end_position().row + 1,
1407        },
1408    })
1409}
1410
1411pub(crate) fn recovered_fragmented_class_has_body(
1412    node: Node<'_>,
1413    source: &str,
1414    expected_name: &str,
1415    expected_range: &Range,
1416) -> bool {
1417    fragmented_class_body(node, source).is_some_and(|recovered| {
1418        recovered.name == expected_name
1419            && recovered.body.class_range.start_byte == expected_range.start_byte
1420            && recovered.body.class_range.end_byte == expected_range.end_byte
1421    })
1422}
1423
1424/// Recover a plain class whose parser-visible body ends inside a malformed
1425/// inline member. Tree-sitter then attaches either the next real member
1426/// declarator or the unfinished `else` branch directly to the outer function
1427/// definition and leaves the class's actual `};` among later siblings. Those
1428/// structured continuations and the close/semicolon siblings establish the
1429/// complete body envelope without interpreting source text.
1430fn fragmented_plain_class_declaration_body<'tree>(
1431    node: Node<'tree>,
1432    source: &str,
1433) -> Option<FragmentedClassRecovery<'tree>> {
1434    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1435        return None;
1436    }
1437    let class_node = node.child_by_field_name("type")?;
1438    if !matches!(
1439        class_node.kind(),
1440        "class_specifier" | "struct_specifier" | "union_specifier"
1441    ) {
1442        return None;
1443    }
1444    let name_node = class_node.child_by_field_name("name")?;
1445    let name = normalize_cpp_whitespace(node_text(name_node, source));
1446    if name.is_empty() || cpp_export_macro_token(&name) {
1447        return None;
1448    }
1449    let body = cpp_body_node(class_node)?;
1450    if body.kind() != "field_declaration_list" {
1451        return None;
1452    }
1453    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1454        if declarator.start_byte() < class_node.end_byte() {
1455            return None;
1456        }
1457        let mut cursor = node.walk();
1458        node.named_children(&mut cursor).any(|child| {
1459            if child.kind() != "ERROR"
1460                || child.start_byte() < class_node.end_byte()
1461                || child.end_byte() > declarator.start_byte()
1462            {
1463                return false;
1464            }
1465            let mut cursor = child.walk();
1466            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1467            let Some((return_type, attributes)) = components.split_last() else {
1468                return false;
1469            };
1470            matches!(
1471                return_type.kind(),
1472                "identifier"
1473                    | "type_identifier"
1474                    | "primitive_type"
1475                    | "decltype"
1476                    | "placeholder_type_specifier"
1477            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1478                && attributes.iter().all(|attribute| {
1479                    matches!(attribute.kind(), "identifier" | "type_identifier")
1480                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1481                            *attribute, source,
1482                        )))
1483                })
1484        })
1485    } else {
1486        let mut cursor = node.walk();
1487        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1488        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1489            if same_node(*candidate_class, class_node)
1490                && continuation.kind() == "identifier"
1491                && node_text(*continuation, source) == "else"
1492                && continuation_body.kind() == "compound_statement"
1493                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1494                && continuation_body
1495                    .child(continuation_body.child_count().saturating_sub(1))
1496                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1497    };
1498    if !displaced_member {
1499        return None;
1500    }
1501    let open = body
1502        .children(&mut body.walk())
1503        .find(|child| child.kind() == "{")?;
1504    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1505    let ordinary_boundary =
1506        siblings
1507            .iter()
1508            .copied()
1509            .enumerate()
1510            .find_map(|(close_index, close)| {
1511                cpp_stray_close_followed_by_semicolon(
1512                    close,
1513                    siblings.get(close_index + 1).copied(),
1514                    source,
1515                )
1516                .map(|semicolon| (close, semicolon))
1517            });
1518    let (close, semicolon) =
1519        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1520            (boundary.class_close, boundary.class_semicolon)
1521        } else {
1522            ordinary_boundary?
1523        };
1524    if open.end_byte() >= close.start_byte() {
1525        return None;
1526    }
1527    Some(FragmentedClassRecovery {
1528        declaration_node: class_node,
1529        name,
1530        raw_supertypes: extract_cpp_supertypes(class_node, source),
1531        body: FragmentedExportBody {
1532            reparse_start: open.end_byte(),
1533            reparse_end: close.start_byte(),
1534            class_range: Range {
1535                start_byte: class_node.start_byte(),
1536                end_byte: semicolon.end_byte(),
1537                start_line: class_node.start_position().row + 1,
1538                end_line: semicolon.end_position().row + 1,
1539            },
1540        },
1541    })
1542}
1543
1544fn displaced_export_function_namespace_shape<'tree>(
1545    declaration: Node<'tree>,
1546    source: &str,
1547) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1548    let mut nested = Vec::new();
1549    for index in (0..declaration.named_child_count()).rev() {
1550        nested.push(declaration.named_child(index)?);
1551    }
1552    while let Some(current) = nested.pop() {
1553        // A recovered export class nested in this class can consume the first
1554        // parser-visible namespace close itself. In that shape the existing
1555        // later-class boundary logic already distinguishes the nested and
1556        // namespace-sibling owners; do not mistake the nested close for this
1557        // class's terminator.
1558        if recover_exported_class_function_definition(current, source).is_some() {
1559            return None;
1560        }
1561        for index in (0..current.named_child_count()).rev() {
1562            nested.push(current.named_child(index)?);
1563        }
1564    }
1565    let mut same_envelope_sibling = declaration.next_named_sibling();
1566    while let Some(current) = same_envelope_sibling {
1567        if recover_exported_class_function_definition(current, source).is_some() {
1568            return None;
1569        }
1570        same_envelope_sibling = current.next_named_sibling();
1571    }
1572    let declaration_list = declaration.parent()?;
1573    let (namespace, class_close, trailing_semicolon) =
1574        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1575    // A chain of malformed export classes can consume one parser-visible
1576    // namespace close per class. Walk through the enclosing sibling levels so
1577    // the later real namespace close remains the structural boundary; a
1578    // direct next-sibling walk stops at the first collapsed namespace and
1579    // incorrectly makes its intervening items members of this class.
1580    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1581    let trailing_index = siblings
1582        .iter()
1583        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1584    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1585        recover_exported_class_function_definition(*candidate, source).is_some()
1586    }) {
1587        // Consecutive recovered classes already have an exact sibling-class
1588        // boundary. Preserve that established path, including nested export
1589        // classes, instead of interpreting the first class close as a
1590        // collapsed namespace boundary.
1591        return None;
1592    }
1593    let mut namespace_items = Vec::new();
1594    let mut nested_fragment_end = 0;
1595    for current in siblings.into_iter().skip(trailing_index + 1) {
1596        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1597        {
1598            return Some(DisplacedFragmentNamespaceBoundary {
1599                class_close,
1600                class_semicolon: trailing_semicolon,
1601                namespace_items,
1602            });
1603        }
1604        if current.start_byte() >= nested_fragment_end
1605            && let Some(recovered) = fragmented_class_body(current, source)
1606        {
1607            nested_fragment_end = recovered.body.class_range.end_byte;
1608        } else if current.start_byte() >= nested_fragment_end
1609            && recover_exported_class_function_definition(current, source).is_some()
1610            && let Some(body) = cpp_body_node(current)
1611            && let Some(fragmented) =
1612                fragmented_export_function_body_region(current, body, source, None)
1613        {
1614            nested_fragment_end = fragmented.class_range.end_byte;
1615        }
1616        namespace_items.push(current);
1617    }
1618    None
1619}
1620
1621fn displaced_fragment_namespace_boundary<'tree>(
1622    declaration: Node<'tree>,
1623    body: Node<'tree>,
1624    source: &str,
1625) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1626    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1627    let reparse_start = body.start_byte() + 1;
1628    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1629    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1630}
1631
1632/// Recover the class/namespace brace geometry for a declaration whose class
1633/// close tree-sitter consumed as the enclosing namespace close. This proof is
1634/// independent of whether every member in the class body can be reparsed: the
1635/// ordinary-tree fallback can still re-own bounded sibling declarations when
1636/// an unknown macro makes the complete body reparse unsafe.
1637fn displaced_fragment_namespace_geometry<'tree>(
1638    declaration: Node<'tree>,
1639    source: &str,
1640) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1641    // A templated class's malformed function wrapper remains beneath the
1642    // template node even though its later members have escaped to the
1643    // enclosing declaration list. Lift only that exact declaration child.
1644    let envelope = declaration
1645        .parent()
1646        .filter(|parent| {
1647            parent.kind() == "template_declaration"
1648                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1649        })
1650        .unwrap_or(declaration);
1651    let declaration_list = envelope.parent()?;
1652    let (_, class_close, trailing_semicolon) =
1653        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1654    let mut namespace_items = Vec::new();
1655    let mut sibling = trailing_semicolon.next_named_sibling();
1656    let mut nested_fragment_end = 0;
1657    loop {
1658        let current = sibling?;
1659        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1660        {
1661            break;
1662        }
1663        if current.start_byte() >= nested_fragment_end
1664            && let Some(recovered) = fragmented_class_body(current, source)
1665        {
1666            nested_fragment_end = recovered.body.class_range.end_byte;
1667        }
1668        namespace_items.push(current);
1669        sibling = current.next_named_sibling();
1670    }
1671    Some(DisplacedFragmentNamespaceBoundary {
1672        class_close,
1673        class_semicolon: trailing_semicolon,
1674        namespace_items,
1675    })
1676}
1677
1678fn cpp_fragment_namespace_boundary_nodes<'tree>(
1679    declaration_list: Node<'tree>,
1680) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1681    if declaration_list.kind() != "declaration_list" {
1682        return None;
1683    }
1684    let namespace = declaration_list.parent()?;
1685    if namespace.kind() != "namespace_definition"
1686        || namespace.child_by_field_name("body") != Some(declaration_list)
1687    {
1688        return None;
1689    }
1690    let class_close = direct_close_brace(declaration_list)?;
1691    let trailing_semicolon = namespace.next_named_sibling()?;
1692    (trailing_semicolon.kind() == "expression_statement"
1693        && trailing_semicolon.named_child_count() == 0)
1694        .then_some((namespace, class_close, trailing_semicolon))
1695}
1696
1697/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1698fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1699    (0..node.child_count())
1700        .filter_map(|index| node.child(index))
1701        .find(|child| !child.is_named() && child.kind() == "}")
1702}
1703
1704/// A displaced lone closing brace: the class close that the fragmented multiple-base
1705/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1706fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1707    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1708}
1709
1710fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1711    let mut name = None;
1712    let mut colon_count = 0;
1713    let mut access_count = 0;
1714    for index in 0..node.child_count() {
1715        let child = node.child(index)?;
1716        match child.kind() {
1717            "identifier" | "type_identifier" if child.is_named() => {
1718                if name.is_some() {
1719                    return None;
1720                }
1721                let candidate = normalize_cpp_whitespace(node_text(child, source));
1722                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1723                    return None;
1724                }
1725                name = Some(candidate);
1726            }
1727            "template_function" | "template_type" if child.is_named() => {
1728                if name.is_some() {
1729                    return None;
1730                }
1731                let candidate = child
1732                    .child_by_field_name("name")
1733                    .and_then(|name| direct_identifier_name(name, source))?;
1734                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1735                    return None;
1736                }
1737                name = Some(candidate);
1738            }
1739            ":" if !child.is_named() => colon_count += 1,
1740            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1741            _ => return None,
1742        }
1743    }
1744    (colon_count == 1 && access_count == 1)
1745        .then_some(name)
1746        .flatten()
1747}
1748
1749fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1750    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1751        return false;
1752    }
1753    node.named_child(0)
1754        .and_then(|child| direct_identifier_name(child, source))
1755        .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1756}
1757
1758use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1759
1760fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1761    match node.kind() {
1762        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1763            recovered_base_atom(node, source)
1764        }
1765        // The macro-declaration grammar wraps an access keyword and the base
1766        // as a `macro_modifier_declarator`; the base is its declarator.
1767        "macro_modifier_declarator" => node
1768            .child_by_field_name("declarator")
1769            .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1770        "template_type" | "template_function" => node
1771            .child_by_field_name("name")
1772            .and_then(|name| recovered_malformed_base_name(name, source)),
1773        "ERROR" => None,
1774        "qualified_identifier" | "scoped_type_identifier" => {
1775            let suffix = node
1776                .child_by_field_name("name")
1777                .and_then(|name| recovered_malformed_base_name(name, source))?;
1778            let scope = node
1779                .child_by_field_name("scope")
1780                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1781            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1782                malformed_qualified_prefix(node, source)?
1783            } else {
1784                if malformed_qualified_prefix(node, source).is_some() {
1785                    return None;
1786                }
1787                scope
1788            };
1789            Some(format!("{prefix}::{suffix}"))
1790        }
1791        _ => None,
1792    }
1793}
1794
1795fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1796    let mut pending = vec![node];
1797    let mut components = Vec::new();
1798    while let Some(current) = pending.pop() {
1799        match current.kind() {
1800            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1801                components.push(recovered_base_atom(current, source)?);
1802            }
1803            "macro_modifier_declarator" => {
1804                pending.push(current.child_by_field_name("declarator")?);
1805            }
1806            "template_type" | "template_function" => {
1807                pending.push(current.child_by_field_name("name")?);
1808            }
1809            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1810                pending.push(current.child_by_field_name("name")?);
1811                if let Some(scope) = current.child_by_field_name("scope") {
1812                    // Malformed export bases can put the access keyword in
1813                    // `scope` and the real scope in the direct ERROR child.
1814                    // Keep the exact AST recovery used by the declaration.
1815                    if recovered_base_atom(scope, source).is_some_and(|name| {
1816                        matches!(name.as_str(), "public" | "protected" | "private")
1817                    }) {
1818                        pending.push(malformed_qualified_prefix_node(current, source)?);
1819                    } else {
1820                        if malformed_qualified_prefix_node(current, source).is_some() {
1821                            return None;
1822                        }
1823                        pending.push(scope);
1824                    }
1825                }
1826            }
1827            _ => return None,
1828        }
1829    }
1830    Some(components)
1831}
1832
1833fn recovered_base_specifier(
1834    node: Node<'_>,
1835    source: &str,
1836    is_virtual: bool,
1837) -> Option<CppBaseSpecifierFact> {
1838    let components = recovered_base_components(node, source)?;
1839    crate::source_facts::base_specifier_fact(
1840        node,
1841        components,
1842        is_virtual,
1843        CppAccessSpecifier::Unknown,
1844    )
1845}
1846
1847fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1848    // Recovery can put the qualifier in an ERROR sibling or inside the base
1849    // node. Read only the AST prefix through this base, resetting at preceding
1850    // inheritance delimiters so another base's qualifier cannot leak here.
1851    let mut pending = vec![container];
1852    let mut is_virtual = false;
1853    while let Some(node) = pending.pop() {
1854        if node.start_byte() >= base.end_byte() {
1855            break;
1856        }
1857        if node.child_count() != 0 {
1858            let mut cursor = node.walk();
1859            let children = node
1860                .children(&mut cursor)
1861                .take_while(|child| child.start_byte() < base.end_byte())
1862                .collect::<Vec<_>>();
1863            pending.extend(children.into_iter().rev());
1864            continue;
1865        }
1866        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1867            is_virtual = false;
1868        } else if node.kind() == "virtual"
1869            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1870        {
1871            is_virtual = true;
1872        }
1873    }
1874    is_virtual
1875}
1876
1877fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1878    if !matches!(
1879        node.kind(),
1880        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1881    ) {
1882        return None;
1883    }
1884    let name = normalize_cpp_whitespace(node_text(node, source));
1885    (!name.is_empty()).then_some(name)
1886}
1887
1888fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1889    malformed_qualified_prefix_node(node, source)
1890        .and_then(|prefix| recovered_base_atom(prefix, source))
1891}
1892
1893fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1894    let mut prefix = None;
1895    let mut cursor = node.walk();
1896    for error in node
1897        .named_children(&mut cursor)
1898        .filter(|child| child.kind() == "ERROR")
1899    {
1900        if prefix.is_some() {
1901            return None;
1902        }
1903        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1904        // the qualified name's scope and leaves `virtual` beside the real
1905        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1906        // component of the base's name (#2924).
1907        let mut error_cursor = error.walk();
1908        let atoms = error
1909            .named_children(&mut error_cursor)
1910            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1911            .collect::<Option<Vec<_>>>()?;
1912        let [atom] = atoms
1913            .iter()
1914            .filter(|(_, name)| name != "virtual")
1915            .collect::<Vec<_>>()[..]
1916        else {
1917            return None;
1918        };
1919        prefix = Some(atom.0);
1920    }
1921    prefix
1922}
1923
1924type RecoveredFunctionDefinitionClass<'tree> = (
1925    Node<'tree>,
1926    String,
1927    Option<Vec<String>>,
1928    Option<Vec<CppBaseSpecifierFact>>,
1929);
1930/// One declaration an attribute-like macro invocation swallowed into a
1931/// declaration-scope `ERROR`, with the byte range that spells it.
1932/// What [`stranded_declaration_run`] read out of one node.
1933struct StrandedRun<'tree> {
1934    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1935    /// Whether every part of the node read as part of a declaration. False when
1936    /// a part the reader does not understand ended it early, or when the last
1937    /// parts were types with no declarator after them. Callers that index what
1938    /// was found keep the declarations either way; a caller deciding whether a
1939    /// whole region is safe to index requires this.
1940    complete: bool,
1941}
1942
1943struct MacroWrappedDeclaration<'tree> {
1944    declarator: Node<'tree>,
1945    range: Range,
1946    /// Whether the recovered declaration spells `static`. The envelope hides
1947    /// that keyword from the ordinary linkage reader, which looks for it among
1948    /// a declaration node's own children, and internal linkage is what decides
1949    /// whether a header declaration and a body in another file are one symbol.
1950    is_static: bool,
1951}
1952
1953/// Whether `node` stands where declarations live: directly in the translation
1954/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1955/// stands in one of those.
1956///
1957/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1958/// where it stands. The case that matters is a whole translation unit the
1959/// parser could not close: whisper.cpp's `include/whisper.h` opens
1960/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1961/// so the braces never balance in the token stream tree-sitter sees, the file's
1962/// own root node is an `ERROR`, and every top-level declaration in the file is
1963/// a child of it (#3094). Without this the macro recoveries below read nothing
1964/// in such a file, because none of their envelopes is in a declaration scope
1965/// the parser managed to build.
1966fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1967    declaration_scope_container(node, ancestry).is_some()
1968}
1969
1970/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1971/// end is where a parse failure that starts at `node` can still be doing
1972/// damage: a declaration scope has no synchronization point of its own, so the
1973/// parser carries such a failure to the scope's close.
1974fn declaration_scope_container<'tree>(
1975    node: Node<'tree>,
1976    ancestry: &ParentIndex<'tree>,
1977) -> Option<Node<'tree>> {
1978    let mut parent = ancestry.parent(node)?;
1979    loop {
1980        match parent.kind() {
1981            "translation_unit" => return Some(parent),
1982            "declaration_list" => {
1983                return ancestry
1984                    .parent(parent)
1985                    .is_some_and(|grandparent| {
1986                        matches!(
1987                            grandparent.kind(),
1988                            "namespace_definition" | "linkage_specification"
1989                        )
1990                    })
1991                    .then_some(parent);
1992            }
1993            "ERROR" => match ancestry.parent(parent) {
1994                Some(grandparent) => parent = grandparent,
1995                // A root `ERROR` is the translation unit the parser could not
1996                // build, so its children stand at file scope.
1997                None => return Some(parent),
1998            },
1999            // A preprocessor conditional is a transparent declaration-scope
2000            // wrapper when it sits directly under the translation unit or a
2001            // namespace/linkage declaration list. Keep climbing through it;
2002            // the next ordinary construct still decides whether the enclosed
2003            // error is at declaration scope. In particular, a conditional
2004            // inside a function reaches its compound statement and is
2005            // rejected below rather than becoming a global recovery region.
2006            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2007                parent = ancestry.parent(parent)?;
2008            }
2009            _ => return None,
2010        }
2011    }
2012}
2013
2014/// Whether `node` is an `ERROR` the parser produced where declarations live.
2015fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2016    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2017}
2018
2019/// The identifier an object-like macro marker wraps. The grammar admits a lone
2020/// identifier as a `macro_modifier` in a declaration's specifier run, or as
2021/// the prefix of a `macro_modifier_declarator`, only where the declaration
2022/// would otherwise not parse, so the modifier is always the structured reading
2023/// of a macro the workspace never expands: it is provenance about the
2024/// declaration, never a declarator of its own.
2025pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2026    if node.kind() != "macro_modifier" {
2027        return None;
2028    }
2029    node.named_child(0)
2030        .filter(|child| child.kind() == "identifier")
2031}
2032
2033/// The modifier word and inner declarator a `macro_modifier_declarator`
2034/// wraps: the grammar's structured reading of an unexpanded object-like macro
2035/// before a declarator. In a statement-context class-body reparse the modifier
2036/// can also be the member's return type, which the grammar could not separate
2037/// from the declarator and admitted as a modifier instead.
2038pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2039    if node.kind() != "macro_modifier_declarator" {
2040        return None;
2041    }
2042    let mut cursor = node.walk();
2043    let modifier = node
2044        .named_children(&mut cursor)
2045        .find(|child| child.kind() == "macro_modifier")
2046        .and_then(macro_modifier_identifier)?;
2047    let declarator = node.child_by_field_name("declarator")?;
2048    Some((modifier, declarator))
2049}
2050
2051/// Whether `node` can only be part of what precedes a declarator -- a type, a
2052/// specifier, or a word of the attribute macro's own text that the lexer left
2053/// as a bare identifier or, under the macro-declaration grammar, wrapped as a
2054/// `macro_modifier` -- so a run of these followed by a declarator is one
2055/// declaration the parser failed to group.
2056fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2057    matches!(
2058        node.kind(),
2059        "identifier"
2060            | "type_identifier"
2061            | "primitive_type"
2062            | "sized_type_specifier"
2063            | "struct_specifier"
2064            | "union_specifier"
2065            | "enum_specifier"
2066            | "type_qualifier"
2067            | "storage_class_specifier"
2068            | "explicit_function_specifier"
2069            | "virtual_function_specifier"
2070            | "qualified_identifier"
2071            | "template_type"
2072            | "dependent_type"
2073            | "placeholder_type_specifier"
2074            | "macro_modifier"
2075            | "macro_type_specifier"
2076    )
2077}
2078
2079/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
2080/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
2081/// words the lexer reports as bare identifiers. Anything else in such an
2082/// `ERROR` stops the recovery, so a macro argument that carries structure this
2083/// recovery does not understand is never guessed at.
2084fn is_macro_argument_error(node: Node<'_>) -> bool {
2085    if node.kind() != "ERROR" {
2086        return false;
2087    }
2088    let mut cursor = node.walk();
2089    node.named_children(&mut cursor).all(|child| {
2090        matches!(
2091            child.kind(),
2092            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2093        )
2094    })
2095}
2096
2097/// The end of a recovered declaration: the declarator's end, extended across
2098/// the `;` the grammar left beside it inside the envelope.
2099///
2100/// The envelope's own end is the hard boundary. The parser hands the last
2101/// declaration's `;` to the sibling statement it recovered with, outside the
2102/// envelope, and a range that reached it would no longer lie inside one node --
2103/// which is how every reader (including the resolver's climb from a range to
2104/// the node that declares it) finds a recovered declaration again.
2105fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2106    declarator
2107        .next_sibling()
2108        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2109        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2110}
2111
2112/// The declarations `node` holds after an attribute-like macro cost the parser
2113/// their grouping, in source order.
2114///
2115/// The parser packs the parts into whichever slots it has left. In whisper's
2116/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
2117/// field takes the export macro, the real return type and the *next*
2118/// declaration's declarator both end up inside one sibling `ERROR`, and the
2119/// `declarator` field takes whatever declaration the recovery reached last. In
2120/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
2121/// arrive as bare identifiers and the attributed member and the member after it
2122/// share one declarator node.
2123///
2124/// Both are the same failure, so both get the same reading: flatten the node's
2125/// parts -- splicing each nested `ERROR`'s own children in place, since an
2126/// `ERROR` here is only a grouping failure -- and read the flat run as what it
2127/// spells, a run of type-and-specifier tokens followed by a declarator, over
2128/// and over.
2129///
2130/// Fails closed. A part that is neither a declarator nor something that can
2131/// only precede one ends the recovery there, and the declarations before it are
2132/// kept.
2133fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2134    let mut parts = Vec::new();
2135    let mut cursor = node.walk();
2136    for child in node.named_children(&mut cursor) {
2137        if child.kind() == "ERROR" {
2138            let mut error_cursor = child.walk();
2139            parts.extend(child.named_children(&mut error_cursor));
2140        } else {
2141            parts.push(child);
2142        }
2143    }
2144
2145    let mut declarations = Vec::new();
2146    let mut start = None;
2147    let mut is_static = false;
2148    let mut complete = true;
2149    for part in parts {
2150        if part.kind() == "comment" {
2151            continue;
2152        }
2153        if let Some(declarator) = extract_function_declarator(part) {
2154            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2155            declarations.push(MacroWrappedDeclaration {
2156                declarator,
2157                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2158                is_static,
2159            });
2160            is_static = false;
2161            continue;
2162        }
2163        if !is_recovered_declaration_type_part(part) {
2164            complete = false;
2165            break;
2166        }
2167        is_static |= part.kind() == "storage_class_specifier"
2168            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2169        start.get_or_insert(part.start_byte());
2170    }
2171    StrandedRun {
2172        declarations,
2173        complete: complete && start.is_none(),
2174    }
2175}
2176
2177/// The declarations an attribute-like macro invocation swallowed into a
2178/// declaration-scope `ERROR`, in source order.
2179///
2180/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2181/// declaration in a macro call:
2182///
2183/// ```text
2184/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2185///                  struct llama_model * model,
2186///           struct llama_context_params   params),
2187///         "use llama_init_from_model instead");
2188/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2189/// ```
2190///
2191/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2192/// holding the macro name, the wrapped declaration as a
2193/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2194/// following declaration as a further `parameter_declaration` until it
2195/// recovers. That is what removed `llama_tokenize` from the index and left the
2196/// seven-argument call in `talk-llama.cpp` with only that file's own
2197/// three-parameter `static` overload to choose from (#2552, and the
2198/// `LLAMA_API` half of #2551).
2199///
2200/// Every part of every swallowed declaration is still a real node; only their
2201/// grouping is lost. This rebuilds the grouping and reads the nodes.
2202///
2203/// Under the macro-declaration grammar the same invocation keeps the same
2204/// `ERROR` envelope, but the parts inside it are structured further: the macro
2205/// head is a whole `function_declarator` whose parameter list holds the
2206/// arguments, an export-macro word is a `macro_modifier`, a following
2207/// declaration is a `macro_modifier_declarator` or a bare
2208/// `function_declarator` in a sibling `ERROR`, and the hint string explodes
2209/// into word nodes between the `ERROR` nodes that hold its quotes. The reading
2210/// is the same either way: the invocation's own arguments, then one
2211/// declaration at a time until something that is not a declaration stops it.
2212fn macro_wrapped_declarations<'tree>(
2213    envelope: Node<'tree>,
2214    source: &str,
2215    ancestry: &ParentIndex<'tree>,
2216) -> Vec<MacroWrappedDeclaration<'tree>> {
2217    let mut declarations = Vec::new();
2218    if !is_declaration_scope_error(envelope, ancestry) {
2219        return declarations;
2220    }
2221    let mut cursor = envelope.walk();
2222    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2223    let [head, rest @ ..] = children.as_slice() else {
2224        return declarations;
2225    };
2226    let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2227        rest.to_vec()
2228    } else if head.kind() == "function_declarator"
2229        && head
2230            .child_by_field_name("declarator")
2231            .is_some_and(|name| name.kind() == "identifier")
2232    {
2233        let Some(parameters) = head
2234            .child_by_field_name("parameters")
2235            .filter(|parameters| parameters.kind() == "parameter_list")
2236        else {
2237            return declarations;
2238        };
2239        let mut parameters_cursor = parameters.walk();
2240        parameters.named_children(&mut parameters_cursor).collect()
2241    } else {
2242        return declarations;
2243    };
2244    let mut wrapped_declaration_seen = false;
2245    // The hint string's exploded words sit between the `ERROR` nodes holding
2246    // its quotes; inside that bracket they are string content, not
2247    // declaration parts, and they must not start a declaration's range.
2248    let mut in_hint = false;
2249    // Where the specifier words gathered for the declaration whose declarator
2250    // has not arrived yet. Only the macro-declaration grammar's envelopes use
2251    // this: in them an export-macro word is a sibling of the declarator
2252    // rather than a part of its `parameter_declaration`.
2253    let mut pending_start = None;
2254    let mut is_static = false;
2255    for argument in arguments {
2256        if argument.kind() == "comment" {
2257            continue;
2258        }
2259        if argument.kind() == "ERROR"
2260            && (0..argument.child_count())
2261                .filter(|index| {
2262                    argument
2263                        .child(*index)
2264                        .is_some_and(|child| child.kind() == "\"")
2265                })
2266                .count()
2267                % 2
2268                == 1
2269        {
2270            // One of the quote `ERROR`s bracketing the hint string.
2271            in_hint = !in_hint;
2272            pending_start = None;
2273            continue;
2274        }
2275        if in_hint {
2276            continue;
2277        }
2278        match argument.kind() {
2279            "parameter_declaration" => {
2280                let recovered = stranded_declaration_run(argument, source).declarations;
2281                if recovered.is_empty() {
2282                    break;
2283                }
2284                wrapped_declaration_seen = true;
2285                declarations.extend(recovered);
2286            }
2287            // A specifier word of the declaration whose declarator the parser
2288            // handed to a later sibling.
2289            "macro_modifier" if wrapped_declaration_seen => {
2290                pending_start.get_or_insert(argument.start_byte());
2291            }
2292            "storage_class_specifier" if wrapped_declaration_seen => {
2293                pending_start.get_or_insert(argument.start_byte());
2294                is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2295            }
2296            "macro_modifier_declarator" if wrapped_declaration_seen => {
2297                let Some(declarator) = extract_function_declarator(argument) else {
2298                    break;
2299                };
2300                let start = pending_start
2301                    .take()
2302                    .unwrap_or_else(|| argument.start_byte());
2303                declarations.push(MacroWrappedDeclaration {
2304                    declarator,
2305                    range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2306                    is_static,
2307                });
2308                is_static = false;
2309            }
2310            // The hint string, and only that: an argument the recovery cannot
2311            // read as a declaration is admitted before the wrapped declaration
2312            // is found, so a macro whose first argument is not a declaration
2313            // recovers nothing.
2314            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2315            // A declaration the parser shredded to a bare function declarator
2316            // and its terminator, its specifier words left as the siblings
2317            // before it.
2318            "ERROR" if wrapped_declaration_seen => {
2319                let mut error_cursor = argument.walk();
2320                let mut parts = argument.named_children(&mut error_cursor);
2321                let Some(declarator) = parts
2322                    .next()
2323                    .filter(|part| part.kind() == "function_declarator")
2324                    .filter(|_| parts.next().is_none())
2325                else {
2326                    break;
2327                };
2328                let start = pending_start
2329                    .take()
2330                    .unwrap_or_else(|| declarator.start_byte());
2331                declarations.push(MacroWrappedDeclaration {
2332                    declarator,
2333                    range: cpp_recovery_window(
2334                        source,
2335                        start,
2336                        recovered_declaration_end(declarator),
2337                    ),
2338                    is_static,
2339                });
2340                is_static = false;
2341            }
2342            _ => break,
2343        }
2344    }
2345    declarations
2346}
2347
2348/// What one macro invocation swallowed when it collapsed a whole run of
2349/// declarations.
2350struct CollapsedMacroDeclarationRun {
2351    /// The byte just past the `;` that closes the invocation, which is where
2352    /// the declarations it swallowed begin.
2353    invocation_end: usize,
2354    /// The byte this recovery owns to: the close of the enclosing declaration
2355    /// scope when the invocation swallowed the declarations written after it,
2356    /// and the invocation's own end when the parser instead spread the
2357    /// invocation itself across the container's children and swallowed nothing.
2358    region_end: usize,
2359}
2360
2361/// The tokens one collapsed macro invocation spans, in source order: the leaves
2362/// of `node`, then the leaves of the siblings written after it. Comments and
2363/// the parser's own MISSING tokens are skipped; neither is in the source the
2364/// invocation is written in.
2365///
2366/// The parser puts the parts of a failed invocation wherever it has room. It
2367/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2368/// `expression_statement` it recovered with; it spreads them flat over the
2369/// container's children; it packs the tail of the file into one of the
2370/// arguments. The grouping therefore says nothing, but the token order still
2371/// spells the invocation, which is what this reads.
2372struct MacroInvocationTokens<'tree> {
2373    stack: Vec<Node<'tree>>,
2374    /// The last sibling handed to `stack`, and the point the next one is asked
2375    /// for. Held as the node rather than as its already-computed successor so
2376    /// the successor is asked for only when the walk actually runs off the end
2377    /// of what it has: `Node::next_sibling` recovers the parent first, which
2378    /// re-descends from the root, so it costs the node's position in the tree.
2379    /// Nearly every node this iterator is built for is rejected on its first
2380    /// token, before any sibling is needed, and computing one eagerly made that
2381    /// rejection cost a walk of the whole container (capstone's generated
2382    /// instruction tables, t19).
2383    frontier: Option<Node<'tree>>,
2384}
2385
2386impl<'tree> MacroInvocationTokens<'tree> {
2387    fn new(node: Node<'tree>) -> Self {
2388        Self {
2389            stack: vec![node],
2390            frontier: Some(node),
2391        }
2392    }
2393}
2394
2395impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2396    type Item = Node<'tree>;
2397
2398    fn next(&mut self) -> Option<Node<'tree>> {
2399        loop {
2400            let Some(node) = self.stack.pop() else {
2401                let sibling = self.frontier?.next_sibling()?;
2402                self.frontier = Some(sibling);
2403                self.stack.push(sibling);
2404                continue;
2405            };
2406            if node.child_count() == 0 {
2407                if node.kind() == "comment" || node.is_missing() {
2408                    continue;
2409                }
2410                return Some(node);
2411            }
2412            let mut cursor = node.walk();
2413            let children = node.children(&mut cursor).collect::<Vec<_>>();
2414            self.stack.extend(children.into_iter().rev());
2415        }
2416    }
2417}
2418
2419/// The macro invocation that begins at `node` when the parser could not group
2420/// it, or `None` when `node` does not begin one.
2421///
2422/// whisper.cpp's bundled `llama.h` writes
2423///
2424/// ```text
2425/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2426///                          const char * path_model,
2427///           struct llama_model_params   params),
2428///         "use llama_model_load_from_file instead");
2429/// ```
2430///
2431/// and its own `include/whisper.h` writes
2432///
2433/// ```text
2434/// WHISPER_DEPRECATED(
2435///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2436///     "use whisper_init_from_file_with_params instead"
2437/// );
2438/// ```
2439///
2440/// tree-sitter cannot know either name is a macro, so it reads the name and its
2441/// `(` as a function declarator whose close it never finds, and carries that
2442/// failure forward over the declarations written after it. In `llama.h` that
2443/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2444/// (#2551). In `whisper.h` it instead flattens the invocations over the
2445/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2446/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2447/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2448/// `function_definition` when a brace block falls in a swallowed tail, because
2449/// the parser borrows it for the body the bogus definition needs, and an
2450/// `ERROR` or a bare token when none does. None of that says anything about the
2451/// construct, so the node's kind is not the criterion; the token order is.
2452///
2453/// The invocation ends at the `)` that closes its own `(`, immediately followed
2454/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2455/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2456/// its own, so a `;` reached before the close means this is some other
2457/// construct -- a macro-defined function body, say -- and the ordinary readers
2458/// keep the node.
2459///
2460/// Fails closed everywhere else too. The head must be an export-macro-shaped
2461/// token followed by `(`, the node must stand where declarations live, and an
2462/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2463/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2464/// nothing past it -- is left to that reader.
2465fn collapsed_macro_declaration_run<'tree>(
2466    node: Node<'tree>,
2467    source: &str,
2468    ancestry: &ParentIndex<'tree>,
2469) -> Option<CollapsedMacroDeclarationRun> {
2470    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2471    let mut tokens = MacroInvocationTokens::new(node);
2472    let name = tokens.next()?;
2473    if !matches!(name.kind(), "identifier" | "type_identifier")
2474        || !cpp_export_macro_token(node_text(name, source))
2475    {
2476        return None;
2477    }
2478    if tokens.next()?.kind() != "(" {
2479        return None;
2480    }
2481    let mut depth = 1usize;
2482    let invocation_end = loop {
2483        let token = tokens.next()?;
2484        match token.kind() {
2485            "(" => depth += 1,
2486            ";" => return None,
2487            ")" => {
2488                depth -= 1;
2489                if depth == 0 {
2490                    let semicolon = tokens.next()?;
2491                    if semicolon.kind() != ";" {
2492                        return None;
2493                    }
2494                    break semicolon.end_byte();
2495                }
2496            }
2497            _ => {}
2498        }
2499    };
2500    // An invocation that reaches the end of its own declaration scope swallowed
2501    // nothing: there is nothing after it left to recover, and the ordinary
2502    // readers have the invocation itself. This is also what stops the scan
2503    // below from re-entering itself, since the region it reparses for one
2504    // invocation ends exactly at that invocation's `;`.
2505    if invocation_end >= container_end {
2506        return None;
2507    }
2508    // Where the damage ends. A node that reaches past its own invocation packed
2509    // real declarations into the invocation's argument list, so the parser was
2510    // still failing when it got there and everything to the close of the scope
2511    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2512    // line 455 with it, and every declaration from there to the end of the file
2513    // is shredded. A node that stops at or before the invocation's `;` holds
2514    // nothing but the invocation, whose own parts the parser spread over the
2515    // siblings after it.
2516    let region_end = if node.end_byte() > invocation_end {
2517        container_end
2518    } else {
2519        invocation_end
2520    };
2521    Some(CollapsedMacroDeclarationRun {
2522        invocation_end,
2523        region_end,
2524    })
2525}
2526
2527/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2528/// ordinary class body, and the members it swallowed.
2529///
2530/// Botan deprecates members that way:
2531///
2532/// ```text
2533/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2534/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2535/// ```
2536///
2537/// The parser reads the macro name as the member's type and its argument list
2538/// as a parenthesized declarator, which then swallows the attributed member
2539/// *and* the member written after it: a `field_declaration` whose `type` is a
2540/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2541/// opening with an `ERROR` whose first part is a bare identifier -- the first
2542/// word of the string, which the lexer could not keep together.
2543///
2544/// The macro's spelling is not the criterion; that structural shape is. The
2545/// returned declarations are in source order, the first being the attributed
2546/// member and the rest the members the declarator swallowed after it.
2547fn string_attribute_macro_member_declarators<'tree>(
2548    field: Node<'tree>,
2549    source: &str,
2550) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2551    if field.kind() != "field_declaration"
2552        || field
2553            .child_by_field_name("type")
2554            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2555    {
2556        return None;
2557    }
2558    let declarator = field.child_by_field_name("declarator")?;
2559    if declarator.kind() != "parenthesized_declarator" {
2560        return None;
2561    }
2562    let opening = declarator.named_child(0)?;
2563    if opening.kind() != "ERROR"
2564        || opening
2565            .named_child(0)
2566            .is_none_or(|word| word.kind() != "identifier")
2567    {
2568        return None;
2569    }
2570    let declarations = stranded_declaration_run(declarator, source).declarations;
2571    (!declarations.is_empty()).then_some(declarations)
2572}
2573
2574/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2575/// export-macro class body leaves in front of the members that macro decorated.
2576///
2577/// The reparse reads the class body as statements, so the attribute becomes a
2578/// call statement of its own -- with the `;` the grammar had to invent -- and
2579/// the members after it are stranded in the `ERROR` that follows.
2580fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2581    let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2582        .then(|| node.named_child(0))
2583        .flatten()
2584        .filter(|child| child.kind() == "call_expression")
2585    else {
2586        return false;
2587    };
2588    call.child_by_field_name("function")
2589        .is_some_and(|function| function.kind() == "identifier")
2590        && call
2591            .child_by_field_name("arguments")
2592            .is_some_and(|arguments| {
2593                let mut cursor = arguments.walk();
2594                arguments.named_child_count() > 0
2595                    && arguments
2596                        .named_children(&mut cursor)
2597                        .all(|argument| argument.kind() == "string_literal")
2598            })
2599}
2600
2601/// The start byte of the call the region reparse left where an access-labeled
2602/// constructor was written.
2603///
2604/// A constructor is a member only inside a class body. The export-macro class
2605/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2606/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2607/// comma-expression under `private:`, with no declarator left in the tree. It
2608/// is still spelled in the source, though, starting at the one call under the
2609/// label whose callee is the class's own name. Botan's private six-parameter
2610/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2611/// call is an ambiguity this declines, and so is a reparse from that byte that
2612/// does not yield exactly one constructor declarator there.
2613fn cpp_access_label_constructor_call_start(
2614    node: Node<'_>,
2615    class_name: &str,
2616    source: &str,
2617) -> Option<usize> {
2618    if node.kind() != "labeled_statement" {
2619        return None;
2620    }
2621    let label = node.named_child(0)?;
2622    if label.kind() != "statement_identifier"
2623        || !matches!(
2624            node_text(label, source).trim(),
2625            "public" | "private" | "protected"
2626        )
2627    {
2628        return None;
2629    }
2630    let mut starts = Vec::new();
2631    let mut stack = vec![node];
2632    while let Some(current) = stack.pop() {
2633        if current.kind() == "call_expression"
2634            && current
2635                .child_by_field_name("function")
2636                .is_some_and(|function| {
2637                    function.kind() == "identifier"
2638                        && node_text(function, source).trim() == class_name
2639                })
2640        {
2641            starts.push(current.start_byte());
2642        }
2643        // The macro-declaration grammar reads the constructor head as a
2644        // declaration whose type is the class name and whose declarators are
2645        // the shredded parameter list and initializers; the head's start is
2646        // the type's start.
2647        if current.kind() == "declaration"
2648            && current
2649                .child_by_field_name("type")
2650                .is_some_and(|type_node| {
2651                    type_node.kind() == "type_identifier"
2652                        && node_text(type_node, source).trim() == class_name
2653                })
2654        {
2655            starts.push(
2656                current
2657                    .child_by_field_name("type")
2658                    .expect("checked declaration type")
2659                    .start_byte(),
2660            );
2661        }
2662        let mut cursor = current.walk();
2663        stack.extend(current.named_children(&mut cursor));
2664    }
2665    let [start] = starts.as_slice() else {
2666        return None;
2667    };
2668    Some(*start)
2669}
2670
2671/// The `function_definition` that gives `declarator` a body, following only the
2672/// declarator chain, so a recovered callable knows whether it is a declaration
2673/// or a definition without anyone having to say.
2674fn cpp_declarator_function_definition<'tree>(
2675    declarator: Node<'tree>,
2676    ancestry: &ParentIndex<'tree>,
2677) -> Option<Node<'tree>> {
2678    let mut current = declarator;
2679    while let Some(parent) = ancestry.parent(current) {
2680        match parent.kind() {
2681            "function_definition" if parent.child_by_field_name("body").is_some() => {
2682                return Some(parent);
2683            }
2684            "pointer_declarator"
2685            | "reference_declarator"
2686            | "parenthesized_declarator"
2687            | "array_declarator" => current = parent,
2688            _ => return None,
2689        }
2690    }
2691    None
2692}
2693
2694/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2695/// declaration walk still reaches, so a recovery that runs ahead of that walk
2696/// would name what it finds without the namespace.
2697fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2698    let mut current = node;
2699    while let Some(parent) = ancestry.parent(current) {
2700        if parent.kind() == "namespace_definition"
2701            && parent.child_by_field_name("body") == Some(current)
2702        {
2703            return true;
2704        }
2705        current = parent;
2706    }
2707    false
2708}
2709
2710/// Whether the source a recovered callable's byte range spells is a definition
2711/// (a declarator with a body) rather than a declaration.
2712///
2713/// A callable recovered from a mangled region owns no node of its own in the
2714/// file's tree, so the occurrence-role scan that climbs from the range to the
2715/// node containing it lands on whatever container the parser left and answers
2716/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2717/// constructor a declaration and left its call site with nothing to navigate to
2718/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2719/// extraction used, and read the answer from the one declaration it spells.
2720///
2721/// `None` when the range is not one recovered declaration on its own, which is
2722/// the case for every ordinary declaration, so callers keep their own answer.
2723pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2724    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2725    let root = tree.root_node();
2726    let mut cursor = root.walk();
2727    let items = root
2728        .named_children(&mut cursor)
2729        .filter(|child| child.kind() != "comment")
2730        .collect::<Vec<_>>();
2731    let [item] = items.as_slice() else {
2732        return None;
2733    };
2734    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2735        return None;
2736    }
2737    match item.kind() {
2738        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2739        "declaration" | "field_declaration" => Some(false),
2740        _ => None,
2741    }
2742}
2743
2744/// Whether `node` is an envelope an attribute-like macro invocation left where
2745/// declarations were written: the declaration-scope `ERROR`
2746/// [`macro_wrapped_declarations`] reads, or the collapsed run
2747/// [`collapsed_macro_declaration_run`] reads.
2748///
2749/// Extraction and resolution must read one definition of this shape. The
2750/// resolver climbs from a declaration's recorded byte range to the node that
2751/// declares it, and a recovered declaration's range lies inside one of these
2752/// envelopes rather than inside a `declaration` node, so the climb stops here
2753/// (the same role `is_recovered_exported_class_container` plays for a recovered
2754/// class).
2755pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2756    let ancestry = ParentIndex::unindexed();
2757    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2758        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2759}
2760
2761fn recover_exported_class_function_definition<'tree>(
2762    node: Node<'tree>,
2763    source: &str,
2764) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2765    if node.kind() != "function_definition" {
2766        return None;
2767    }
2768    if let Some(prefix) = node.prev_named_sibling()
2769        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2770        && recovered.range.end_byte == node.end_byte()
2771    {
2772        return Some((
2773            node,
2774            recovered.name,
2775            recovered.raw_supertypes,
2776            recovered.base_facts,
2777        ));
2778    }
2779    let type_node = node.child_by_field_name("type")?;
2780    let declarator = node.child_by_field_name("declarator")?;
2781
2782    if matches!(
2783        type_node.kind(),
2784        "class_specifier" | "struct_specifier" | "union_specifier"
2785    ) {
2786        let type_name = type_node
2787            .child_by_field_name("name")
2788            .and_then(|name| direct_identifier_name(name, source));
2789        let exported_macro_type = type_name
2790            .as_ref()
2791            .is_some_and(|name| cpp_export_macro_token(name));
2792        if exported_macro_type {
2793            let mut cursor = node.walk();
2794            let errors_before_declarator = node
2795                .named_children(&mut cursor)
2796                .filter(|child| {
2797                    child.kind() == "ERROR"
2798                        && child.start_byte() >= type_node.end_byte()
2799                        && child.end_byte() <= declarator.start_byte()
2800                })
2801                .collect::<Vec<_>>();
2802            if let Some(name) = errors_before_declarator
2803                .iter()
2804                .find_map(|error| displaced_exported_class_name(*error, source))
2805            {
2806                let base_node = errors_before_declarator
2807                    .iter()
2808                    .any(|error| malformed_inheritance_syntax(*error))
2809                    .then_some(declarator);
2810                let raw_supertypes = base_node
2811                    .and_then(|base| recovered_malformed_base_name(base, source))
2812                    .map(|base| vec![base]);
2813                let base_facts = base_node
2814                    .and_then(|base| {
2815                        recovered_base_specifier(
2816                            base,
2817                            source,
2818                            recovered_base_is_virtual(node, base, source),
2819                        )
2820                    })
2821                    .map(|base| vec![base]);
2822                return Some((node, name, raw_supertypes, base_facts));
2823            }
2824            if errors_before_declarator
2825                .iter()
2826                .any(|error| malformed_inheritance_syntax(*error))
2827            {
2828                return None;
2829            }
2830        }
2831        if !exported_macro_type
2832            && let Some(name) = type_name
2833            && !cpp_export_macro_token(&name)
2834            && let Some(base) =
2835                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2836        {
2837            let base_node =
2838                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2839            let base_facts = Some(vec![recovered_base_specifier(
2840                base_node,
2841                source,
2842                recovered_base_is_virtual(node, base_node, source),
2843            )?]);
2844            return Some((node, name, Some(vec![base]), base_facts));
2845        }
2846        // The macro-declaration grammar reads the displaced class name and the
2847        // `final` that follows it as a `macro_modifier_declarator`; read the
2848        // name positionally from the head's tokens, the same rule the other
2849        // recovered export heads use (#2557).
2850        let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2851            (declarator.kind() == "macro_modifier_declarator")
2852                .then(|| node.child_by_field_name("body"))
2853                .flatten()
2854                .filter(|body| body.kind() == "compound_statement")
2855                .and_then(|body| recovered_export_head_name(node, body, source))
2856                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2857        });
2858        if let Some(name) = declarator_name
2859            && exported_macro_type
2860            && !cpp_export_macro_token(&name)
2861        {
2862            let base_node = exported_macro_type
2863                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2864                .flatten();
2865            let raw_supertypes = base_node
2866                .and_then(|base| recovered_malformed_base_name(base, source))
2867                .map(|base| vec![base]);
2868            let base_facts = base_node
2869                .and_then(|base| {
2870                    recovered_base_specifier(
2871                        base,
2872                        source,
2873                        recovered_base_is_virtual(node, base, source),
2874                    )
2875                })
2876                .map(|base| vec![base]);
2877            return Some((node, name, raw_supertypes, base_facts));
2878        }
2879        if declarator.kind() == "parenthesized_declarator"
2880            && type_node
2881                .child_by_field_name("name")
2882                .and_then(|name| direct_identifier_name(name, source))
2883                .is_some_and(|name| cpp_export_macro_token(&name))
2884        {
2885            if let Some((name, base_node)) =
2886                recovered_function_like_export_class_owner_node(declarator, source)
2887            {
2888                return Some((
2889                    node,
2890                    name,
2891                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2892                    Some(vec![recovered_base_specifier(
2893                        base_node,
2894                        source,
2895                        recovered_base_is_virtual(node, base_node, source),
2896                    )?]),
2897                ));
2898            }
2899            let body_start = node
2900                .child_by_field_name("body")
2901                .map(|body| body.start_byte())
2902                .unwrap_or(node.end_byte());
2903            let mut cursor = node.walk();
2904            if let Some(name) = node
2905                .named_children(&mut cursor)
2906                .filter(|child| {
2907                    child.kind() == "ERROR"
2908                        && child.start_byte() >= declarator.end_byte()
2909                        && child.end_byte() <= body_start
2910                })
2911                .find_map(|error| declarator_name_from_node(error, source))
2912            {
2913                return Some((node, name, None, None));
2914            }
2915            // The macro-declaration grammar keeps a head without a base clause
2916            // whole inside the declarator: `( ERROR(2, 2)) TOTP final` with a
2917            // missing `)`, the name and `final` wrapped as a
2918            // `macro_modifier_declarator` or the name left bare. Read the name
2919            // positionally from the declarator's own tokens (#2557). The macro
2920            // is the `type` field, so a capitalized name (`TOTP`) is the class.
2921            if let Some(body) = node
2922                .child_by_field_name("body")
2923                .filter(|body| body.kind() == "compound_statement")
2924                && let Some(name_node) =
2925                    export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2926            {
2927                let name = normalize_cpp_whitespace(node_text(name_node, source));
2928                if !name.is_empty() {
2929                    let (bases, base_facts) = recovered_export_head_base_data(
2930                        declarator,
2931                        name_node.end_byte(),
2932                        body.start_byte(),
2933                        source,
2934                    );
2935                    return Some((
2936                        node,
2937                        name,
2938                        (!bases.is_empty()).then_some(bases),
2939                        (!base_facts.is_empty()).then_some(base_facts),
2940                    ));
2941                }
2942            }
2943        }
2944    }
2945
2946    // The macro-declaration grammar reads a namespace-opening macro before an
2947    // export-macro class head (`NS_ENTER(ns) class API Service { ... }`) with
2948    // the invocation as the `macro_type_specifier` type and the class head as
2949    // a `macro_modifier_declarator` chain. The class name is the last head
2950    // token before the body, per the positional rule (#2557).
2951    if type_node.kind() == "macro_type_specifier"
2952        && declarator.kind() == "macro_modifier_declarator"
2953        && type_node
2954            .child_by_field_name("name")
2955            .and_then(|name| direct_identifier_name(name, source))
2956            .is_some_and(|name| cpp_export_macro_token(&name))
2957    {
2958        let body = node
2959            .child_by_field_name("body")
2960            .filter(|body| body.kind() == "compound_statement")?;
2961        let tokens = export_head_tokens(node, Some(body));
2962        let has_class_keyword = tokens.iter().any(|token| {
2963            matches!(token.kind(), "identifier" | "type_identifier")
2964                && matches!(node_text(*token, source), "class" | "struct" | "union")
2965        });
2966        if !has_class_keyword {
2967            return None;
2968        }
2969        let name_node = recovered_export_head_name(node, body, source)?;
2970        let name = normalize_cpp_whitespace(node_text(name_node, source));
2971        if name.is_empty() || cpp_export_macro_token(&name) {
2972            return None;
2973        }
2974        return Some((node, name, None, None));
2975    }
2976
2977    let declarator_text = direct_identifier_name(declarator, source)?;
2978    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2979        return None;
2980    }
2981    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2982}
2983
2984fn recovered_function_like_export_class_owner_node<'tree>(
2985    declarator: Node<'tree>,
2986    source: &str,
2987) -> Option<(String, Node<'tree>)> {
2988    if declarator.kind() != "parenthesized_declarator" {
2989        return None;
2990    }
2991    let mut cursor = declarator.walk();
2992    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2993    let [prefix, base] = children.as_slice() else {
2994        return None;
2995    };
2996    if prefix.kind() != "ERROR"
2997        || !matches!(
2998            base.kind(),
2999            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3000        )
3001    {
3002        return None;
3003    }
3004    let mut identifiers = Vec::new();
3005    let mut prefix_cursor = prefix.walk();
3006    let mut parts = prefix
3007        .named_children(&mut prefix_cursor)
3008        .collect::<Vec<_>>();
3009    // The macro-declaration grammar wraps the displaced name and its `final`
3010    // as a `macro_modifier_declarator`; read the identifiers it wraps the same
3011    // way as the bare ones the older grammar left here.
3012    while let Some(child) = parts.pop() {
3013        match child.kind() {
3014            "number_literal" | "string_literal" | "char_literal" => {}
3015            "identifier" | "type_identifier" => {
3016                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3017            }
3018            "macro_modifier" => {
3019                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3020            }
3021            "macro_modifier_declarator" => {
3022                parts.extend(child.named_children(&mut child.walk()));
3023            }
3024            _ => return None,
3025        }
3026    }
3027    identifiers.reverse();
3028    let name = match identifiers.as_slice() {
3029        [name] => name.clone(),
3030        [name, final_token] if final_token == "final" => name.clone(),
3031        _ => return None,
3032    };
3033    if name.is_empty() || cpp_export_macro_token(&name) {
3034        return None;
3035    }
3036    Some((name, *base))
3037}
3038
3039/// Collect the base names and source facts tree-sitter scattered across the
3040/// recovered head of a function-like export-macro class. A base arrives either
3041/// as a direct sibling identifier or inside the `ERROR` node the grammar
3042/// produced for a `: public Base` fragment. The grammar leaves the `final`
3043/// specifier and access specifiers in the same position as the bases, so drop
3044/// them.
3045/// The bases a recovered function-like export-macro class head names between
3046/// `after` (the end of the class name, or of the access specifier that stands
3047/// in for it) and `before` (the start of the body, or of the `init_declarator`
3048/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
3049/// fragments, and one fragment can also hold the class name and `final`
3050/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
3051/// than by the fragment that holds it.
3052fn recovered_export_head_base_data(
3053    node: Node<'_>,
3054    after: usize,
3055    before: usize,
3056    source: &str,
3057) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3058    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3059    let mut base_nodes = Vec::new();
3060    let mut cursor = node.walk();
3061    for child in node.named_children(&mut cursor) {
3062        if child.kind() == "ERROR" {
3063            let mut error_cursor = child.walk();
3064            base_nodes.extend(
3065                child
3066                    .named_children(&mut error_cursor)
3067                    .filter(within)
3068                    .filter_map(|part| {
3069                        recovered_malformed_base_name(part, source).map(|_| (part, child))
3070                    }),
3071            );
3072        } else if child.kind() == "macro_modifier_declarator" {
3073            // The modifier words are access specifiers or decoration; the base
3074            // is the wrapper's terminal declarator.
3075            let mut current = child;
3076            while let Some(inner) = current.child_by_field_name("declarator") {
3077                if inner.kind() == "macro_modifier_declarator" {
3078                    current = inner;
3079                    continue;
3080                }
3081                if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3082                    base_nodes.push((inner, child));
3083                }
3084                break;
3085            }
3086        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3087            base_nodes.push((child, node));
3088        }
3089    }
3090    let mut bases = Vec::new();
3091    let mut facts = Vec::new();
3092    for (base, container) in base_nodes {
3093        let Some(name) = recovered_malformed_base_name(base, source) else {
3094            continue;
3095        };
3096        if matches!(
3097            name.as_str(),
3098            "final" | "public" | "protected" | "private" | "virtual"
3099        ) {
3100            continue;
3101        }
3102        bases.push(name);
3103        if let Some(fact) = recovered_base_specifier(
3104            base,
3105            source,
3106            recovered_base_is_virtual(container, base, source),
3107        ) {
3108            facts.push(fact);
3109        }
3110    }
3111    (bases, facts)
3112}
3113
3114fn recovered_export_head_bases(
3115    node: Node<'_>,
3116    after: usize,
3117    before: usize,
3118    source: &str,
3119) -> Vec<String> {
3120    recovered_export_head_base_data(node, after, before, source).0
3121}
3122
3123/// Whether `token` is the `final` that closes a recovered class head. The
3124/// grammar keeps it as the `final` keyword when it recognised the head
3125/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
3126/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
3127fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3128    if token.is_named() {
3129        token.kind() == "identifier" && node_text(token, source) == "final"
3130    } else {
3131        token.kind() == "final"
3132    }
3133}
3134
3135/// The class name of a recovered function-like export-macro class head, read by
3136/// position rather than spelling (#2557). `node` is the sibling tree-sitter
3137/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
3138/// carries the body. The head's tokens, in source order with `ERROR` fragments
3139/// flattened, read `macros... name [final] [: bases...]`: the name is the last
3140/// identifier before the head ends, and the head ends at `final`, at the base
3141/// clause `:`, or at `tail`. Every identifier before the name is decoration
3142/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
3143/// (`X509_CA`) is a class name like any other.
3144///
3145/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
3146/// rule, not a token of the source, so it is never the name.
3147fn recovered_export_head_name<'tree>(
3148    node: Node<'tree>,
3149    tail: Node<'tree>,
3150    source: &str,
3151) -> Option<Node<'tree>> {
3152    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3153}
3154
3155/// The class name of a head whose tokens straddle the recovered pair: the
3156/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
3157/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
3158/// The positional rule is the same, read over the two token runs in source
3159/// order, so a name in either node wins by position rather than by which node
3160/// holds it.
3161fn recovered_export_pair_head_name<'tree>(
3162    prefix: Node<'tree>,
3163    sibling: Node<'tree>,
3164    tail: Node<'tree>,
3165    source: &str,
3166) -> Option<Node<'tree>> {
3167    let mut tokens = export_head_tokens(prefix, None);
3168    tokens.extend(export_head_tokens(sibling, Some(tail)));
3169    export_head_name_from_tokens(&tokens, source)
3170}
3171
3172/// One node's head tokens in source order, with `ERROR` fragments spliced in
3173/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
3174/// before `tail` when the caller names one. A `macro_modifier` or
3175/// `macro_modifier_declarator` is the macro-declaration grammar's structured
3176/// form of the same grouping failure, so its children join the run in place
3177/// the same way, recursively.
3178fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3179    let mut tokens = Vec::new();
3180    let mut stack = Vec::new();
3181    let mut cursor = node.walk();
3182    stack.extend(
3183        node.children(&mut cursor)
3184            .collect::<Vec<_>>()
3185            .into_iter()
3186            .rev(),
3187    );
3188    while let Some(child) = stack.pop() {
3189        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3190            break;
3191        }
3192        if matches!(
3193            child.kind(),
3194            "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3195        ) {
3196            let mut fragment_cursor = child.walk();
3197            stack.extend(
3198                child
3199                    .children(&mut fragment_cursor)
3200                    .collect::<Vec<_>>()
3201                    .into_iter()
3202                    .rev(),
3203            );
3204        } else {
3205            tokens.push(child);
3206        }
3207    }
3208    tokens
3209}
3210
3211/// The last identifier before the head ends, per the positional rule.
3212fn export_head_name_from_tokens<'tree>(
3213    tokens: &[Node<'tree>],
3214    source: &str,
3215) -> Option<Node<'tree>> {
3216    let end = tokens
3217        .iter()
3218        .position(|token| {
3219            recovered_export_head_final(*token, source)
3220                || (!token.is_named() && token.kind() == ":")
3221        })
3222        .unwrap_or(tokens.len());
3223    export_head_last_identifier(tokens[..end].iter().copied())
3224}
3225
3226fn export_head_last_identifier<'tree>(
3227    candidates: impl IntoIterator<Item = Node<'tree>>,
3228) -> Option<Node<'tree>> {
3229    candidates
3230        .into_iter()
3231        .filter(|candidate| {
3232            candidate.is_named()
3233                && !candidate.is_missing()
3234                && matches!(
3235                    candidate.kind(),
3236                    "identifier" | "type_identifier" | "field_identifier"
3237                )
3238        })
3239        .last()
3240}
3241
3242/// The `init_declarator` whose `value` carries the class body when the grammar
3243/// keeps a function-like export-macro class head as one `declaration`.
3244fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3245    let mut cursor = declaration.walk();
3246    declaration
3247        .named_children(&mut cursor)
3248        .find(|child| child.kind() == "init_declarator")
3249}
3250
3251/// Read the bases and the initializer-list body of a `declaration`-shaped tail
3252/// of a function-like export-macro class head. The grammar splits a base list
3253/// across bare declarator fields, `ERROR` fragments, and one trailing
3254/// `init_declarator` whose `value` is the class body. `head_end` is where the
3255/// class identity ends and the bases can begin: the end of the access specifier
3256/// when the class name went to a statement label, and the end of the class name
3257/// itself otherwise.
3258fn recovered_export_declaration_tail<'tree>(
3259    declaration: Node<'tree>,
3260    head_end: usize,
3261    source: &str,
3262) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3263    let init = recovered_export_init_declarator(declaration)?;
3264    let body = init.child_by_field_name("value")?;
3265    if body.kind() != "initializer_list" {
3266        return None;
3267    }
3268    let (mut bases, mut facts) =
3269        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3270    let (more_bases, more_facts) =
3271        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3272    bases.extend(more_bases);
3273    facts.extend(more_facts);
3274    Some((bases, (!facts.is_empty()).then_some(facts), body))
3275}
3276
3277/// Whether `node` is the head fragment tree-sitter leaves where
3278/// `class MACRO(args)` was written: the class keyword and the export macro's
3279/// function-like invocation in that order, with nothing else between them.
3280/// Spelling plays no part -- the invocation's `(` following the macro
3281/// identifier directly is what makes it function-like (#2557).
3282///
3283/// Two reductions produce the head. On its own the grammar keeps a body-less
3284/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
3285/// with the invocation's `(`, arguments and `)` beside it. After object-like
3286/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
3287/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
3288/// `declaration` whose class keyword is demoted to a bare `identifier` and
3289/// whose macro invocation becomes the trailing `init_declarator` (#2924).
3290fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3291    match node.kind() {
3292        "ERROR" => {
3293            let Some(class_node) = first_class_like_child(node) else {
3294                return false;
3295            };
3296            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3297                return false;
3298            }
3299            if class_node
3300                .child_by_field_name("name")
3301                .and_then(|name| direct_identifier_name(name, source))
3302                .is_none()
3303            {
3304                return false;
3305            }
3306            class_node
3307                .next_sibling()
3308                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3309        }
3310        "declaration" => {
3311            let mut cursor = node.walk();
3312            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3313            // The class keyword lost its own node: it survives as a bare
3314            // identifier spelled `class`, `struct` or `union`, which is never a
3315            // declarator or a type in well-formed code. It can sit inside an
3316            // `ERROR` beside the object-like macro names, and an `ERROR` here is
3317            // a grouping failure rather than a construct, so read through it.
3318            let Some(keyword) = children
3319                .iter()
3320                .copied()
3321                .flat_map(|child| {
3322                    let mut cursor = child.walk();
3323                    if child.kind() == "ERROR" {
3324                        child.named_children(&mut cursor).collect::<Vec<_>>()
3325                    } else {
3326                        vec![child]
3327                    }
3328                })
3329                .find(|child| {
3330                    child.kind() == "identifier"
3331                        && matches!(node_text(*child, source), "class" | "struct" | "union")
3332                })
3333            else {
3334                return false;
3335            };
3336            children.last().is_some_and(|init| {
3337                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3338                    let mut cursor = init.walk();
3339                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3340                    matches!(parts.as_slice(), [macro_name, arguments]
3341                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
3342                            && arguments.kind() == "argument_list")
3343                }
3344            })
3345        }
3346        _ => false,
3347    }
3348}
3349
3350fn recover_function_like_export_class_pair(
3351    node: Node<'_>,
3352    source: &str,
3353) -> Option<RecoveredFunctionLikeExportClassPair> {
3354    if !is_function_like_export_class_head(node, source) {
3355        return None;
3356    }
3357    let sibling = node.next_named_sibling()?;
3358    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3359        // At translation-unit scope tree-sitter can leave the malformed class
3360        // head in one ERROR node and parse its body as the adjacent compound
3361        // statement. The head still carries the export invocation, displaced
3362        // class name, and optional `final` token in source order, so recover
3363        // the name by the same positional rule as the other shapes.
3364        "compound_statement" => (
3365            recovered_export_pair_head_name(node, sibling, sibling, source)
3366                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3367            None,
3368            None,
3369            sibling,
3370        ),
3371        "expression_statement" => {
3372            let compound = sibling.named_child(0)?;
3373            if compound.kind() != "compound_literal_expression" {
3374                return None;
3375            }
3376            let body = compound.child_by_field_name("value")?;
3377            if body.kind() != "initializer_list" {
3378                return None;
3379            }
3380            (
3381                compound
3382                    .child_by_field_name("type")
3383                    .and_then(|name| direct_identifier_name(name, source))?,
3384                None,
3385                None,
3386                body,
3387            )
3388        }
3389        "labeled_statement" => {
3390            let label = sibling.child_by_field_name("label")?;
3391            if label.kind() != "statement_identifier" {
3392                return None;
3393            }
3394            let name = normalize_cpp_whitespace(node_text(label, source));
3395            let declaration = sibling
3396                .named_children(&mut sibling.walk())
3397                .find(|child| child.kind() == "declaration")?;
3398            let access = declaration.child_by_field_name("type")?;
3399            if !matches!(
3400                node_text(access, source),
3401                "public" | "protected" | "private"
3402            ) {
3403                return None;
3404            }
3405            let (bases, base_facts, body) =
3406                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3407            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3408        }
3409        // `class MACRO(2, 0) Name final { ... };`,
3410        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3411        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3412        // spread over the `type` field, the declarator field, and `ERROR`
3413        // fragments beside them; the name is the last one before the head ends.
3414        "function_definition" => {
3415            let body = sibling.child_by_field_name("body")?;
3416            if body.kind() != "compound_statement" {
3417                return None;
3418            }
3419            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3420            let (bases, base_facts) = recovered_export_head_base_data(
3421                sibling,
3422                name_node.end_byte(),
3423                body.start_byte(),
3424                source,
3425            );
3426            (
3427                normalize_cpp_whitespace(node_text(name_node, source)),
3428                (!bases.is_empty()).then_some(bases),
3429                (!base_facts.is_empty()).then_some(base_facts),
3430                body,
3431            )
3432        }
3433        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3434        // comma-separated base list makes the grammar keep the whole tail as one
3435        // declaration whose trailing `init_declarator` carries the body.
3436        "declaration" => {
3437            let init = recovered_export_init_declarator(sibling)?;
3438            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3439            let (bases, base_facts, body) =
3440                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3441            (
3442                normalize_cpp_whitespace(node_text(name_node, source)),
3443                (!bases.is_empty()).then_some(bases),
3444                base_facts,
3445                body,
3446            )
3447        }
3448        _ => return None,
3449    };
3450    debug_assert!(
3451        !name.is_empty(),
3452        "a recovered class head names its class by an identifier token"
3453    );
3454    let range = Range {
3455        start_byte: node.start_byte(),
3456        end_byte: sibling.end_byte(),
3457        start_line: node.start_position().row + 1,
3458        end_line: sibling.end_position().row + 1,
3459    };
3460    Some(RecoveredFunctionLikeExportClassPair {
3461        name,
3462        raw_supertypes,
3463        base_facts,
3464        range,
3465        fragmented_body: recovered_fragmented_export_body(body, range)?,
3466    })
3467}
3468
3469/// Recover a function-like export-macro class that tree-sitter embedded in a
3470/// larger error after an earlier malformed class body. The grammar still
3471/// preserves every part of the class head: the `class` token, export macro
3472/// identifier and argument list, displaced class identifier, access specifier,
3473/// base field, and initializer-list-shaped body. Match only that complete
3474/// structured sequence and keep each recovered class's exact byte envelope.
3475fn recover_embedded_function_like_export_classes(
3476    node: Node<'_>,
3477    source: &str,
3478) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3479    if !node.is_error() {
3480        return Vec::new();
3481    }
3482
3483    let mut nodes = Vec::new();
3484    let mut class_tokens = Vec::new();
3485    let mut stack = vec![node];
3486    while let Some(current) = stack.pop() {
3487        nodes.push(current);
3488        // An error subtree without an anonymous class token cannot contain
3489        // a recoverable class head. Detect that before sorting or resolving
3490        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3491        if !current.is_named() && current.kind() == "class" {
3492            class_tokens.push(current);
3493        }
3494        push_children_reversed(current, &mut stack);
3495    }
3496    if class_tokens.is_empty() {
3497        return Vec::new();
3498    }
3499    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3500    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3501
3502    // The scans below ask the same handful of kind questions once per node of
3503    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3504    let language = node.language();
3505    let class_kind = NodeKindIds::new(&language, "class");
3506    let identifier_kinds = [
3507        NodeKindIds::new(&language, "identifier"),
3508        NodeKindIds::new(&language, "type_identifier"),
3509        NodeKindIds::new(&language, "field_identifier"),
3510    ];
3511    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3512    let colon_kind = NodeKindIds::new(&language, ":");
3513    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3514
3515    let mut recovered = Vec::new();
3516    for class_token in class_tokens {
3517        debug_assert!(class_kind.matches(class_token));
3518        let row = class_token.start_position().row;
3519        // The head after the `class` token reads `MACRO(args) macros... name
3520        // [final] : bases`. The macro is the first identifier, a function-like
3521        // invocation when its argument list is the next thing after it, and the
3522        // name is the last identifier before the head ends at `final` or at the
3523        // base clause `:`. Spelling plays no part (#2557).
3524        let is_identifier = |candidate: &Node<'_>| {
3525            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3526        };
3527        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3528            candidate.start_byte() >= class_token.end_byte()
3529                && candidate.start_position().row == row
3530                && is_identifier(candidate)
3531        }) else {
3532            continue;
3533        };
3534        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3535            argument_list_kind.matches(*candidate)
3536                && candidate.start_byte() >= macro_name.end_byte()
3537                && candidate.start_position().row == row
3538        }) else {
3539            continue;
3540        };
3541        if nodes.iter().any(|candidate| {
3542            is_identifier(candidate)
3543                && candidate.start_byte() >= macro_name.end_byte()
3544                && candidate.end_byte() <= arguments.start_byte()
3545        }) {
3546            continue;
3547        }
3548        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3549            candidate.start_byte() >= arguments.end_byte()
3550                && (recovered_export_head_final(*candidate, source)
3551                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3552        }) else {
3553            continue;
3554        };
3555        let Some(name_node) =
3556            export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3557                is_identifier(candidate)
3558                    && candidate.start_byte() >= arguments.end_byte()
3559                    && candidate.end_byte() <= head_end.start_byte()
3560                    && candidate.start_position().row == row
3561            }))
3562        else {
3563            continue;
3564        };
3565        let name = normalize_cpp_whitespace(node_text(name_node, source));
3566        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3567            field_initializer_kind.matches(*candidate)
3568                && candidate.start_byte() >= name_node.end_byte()
3569                && candidate
3570                    .child_by_field_name("field")
3571                    .or_else(|| candidate.named_child(0))
3572                    .is_some()
3573                && candidate
3574                    .child_by_field_name("value")
3575                    .or_else(|| {
3576                        let mut cursor = candidate.walk();
3577                        candidate
3578                            .named_children(&mut cursor)
3579                            .find(|child| child.kind() == "initializer_list")
3580                    })
3581                    .is_some_and(|value| value.kind() == "initializer_list")
3582        }) else {
3583            continue;
3584        };
3585        let has_access = nodes.iter().copied().any(|candidate| {
3586            candidate.start_byte() >= name_node.end_byte()
3587                && candidate.end_byte() <= base_initializer.start_byte()
3588                && matches!(
3589                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3590                    "public" | "protected" | "private"
3591                )
3592        });
3593        if !has_access {
3594            continue;
3595        }
3596        let Some(base_node) = base_initializer
3597            .child_by_field_name("field")
3598            .or_else(|| base_initializer.named_child(0))
3599        else {
3600            continue;
3601        };
3602        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3603            continue;
3604        };
3605        let body = base_initializer
3606            .child_by_field_name("value")
3607            .or_else(|| {
3608                let mut cursor = base_initializer.walk();
3609                base_initializer
3610                    .named_children(&mut cursor)
3611                    .find(|child| child.kind() == "initializer_list")
3612            })
3613            .expect("initializer-list value checked above");
3614        let range = Range {
3615            start_byte: class_token.start_byte(),
3616            end_byte: body.end_byte(),
3617            start_line: class_token.start_position().row + 1,
3618            end_line: body.end_position().row + 1,
3619        };
3620        if recovered
3621            .iter()
3622            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3623                existing.name == name && existing.range == range
3624            })
3625        {
3626            continue;
3627        }
3628        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3629            continue;
3630        };
3631        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3632            name,
3633            range,
3634            raw_supertypes: vec![base],
3635            base_facts: vec![base_fact],
3636            fragmented_body: match recovered_fragmented_export_body(body, range) {
3637                Some(fragmented) => fragmented,
3638                None => continue,
3639            },
3640        });
3641    }
3642    recovered
3643}
3644
3645fn lifted_function_like_export_class_namespace<'tree>(
3646    node: Node<'tree>,
3647    source: &str,
3648    ancestry: &ParentIndex<'tree>,
3649) -> Option<String> {
3650    // A long malformed body can embed the next exported class several levels
3651    // below a bogus top-level function_definition. Compare namespace evidence
3652    // against that top-level envelope, not only the recovered ERROR's direct
3653    // parent. The source tree still proves the same boundary: one earlier
3654    // malformed namespace and one later standalone closing brace.
3655    let mut anchor = node;
3656    let parent = loop {
3657        let parent = ancestry.parent(anchor)?;
3658        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3659            break parent;
3660        }
3661        anchor = parent;
3662    };
3663    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3664        sibling.start_byte() > anchor.end_byte()
3665            && sibling.kind() == "ERROR"
3666            && sibling.named_child_count() == 0
3667            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3668    });
3669    if !has_later_close {
3670        return None;
3671    }
3672    let candidates = parent
3673        .named_children(&mut parent.walk())
3674        .filter(|sibling| {
3675            sibling.kind() == "namespace_definition"
3676                && sibling.has_error()
3677                && sibling.end_byte() < anchor.start_byte()
3678        })
3679        .filter_map(|namespace| {
3680            namespace
3681                .child_by_field_name("name")
3682                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3683                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3684        })
3685        .collect::<Vec<_>>();
3686    let [namespace] = candidates.as_slice() else {
3687        return None;
3688    };
3689    Some(namespace.clone())
3690}
3691
3692pub(crate) fn recovered_function_like_export_class_pair_has_body(
3693    node: Node<'_>,
3694    source: &str,
3695    identifier: &str,
3696    range: &Range,
3697) -> bool {
3698    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3699        recovered.name == identifier
3700            && recovered.range.start_byte == range.start_byte
3701            && recovered.range.end_byte == range.end_byte
3702    })
3703}
3704
3705fn recovered_exported_class_has_body(
3706    node: Node<'_>,
3707    source: &str,
3708    expected_name: &str,
3709) -> Option<bool> {
3710    match node.kind() {
3711        "function_definition" => {
3712            let (class_node, name, _, _) =
3713                recover_exported_class_function_definition(node, source)?;
3714            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3715        }
3716        "declaration" | "field_declaration" => {
3717            let recovered = recover_exported_class_declaration(node, source)?;
3718            (recovered.name == expected_name).then(|| recovered.body.is_some())
3719        }
3720        _ => None,
3721    }
3722}
3723
3724/// One file's embedded export-macro class recovery, resolved once and keyed by
3725/// the `ERROR` node that mints each set.
3726///
3727/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3728/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3729/// once per class-like unit in the file. On a translation unit the parser could
3730/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3731/// node spans most of the file -- that is one full subtree pass per class,
3732/// quadratic in the file's size, and it was 78% of that file's inverse scan
3733/// (#1496).
3734///
3735/// Keyed by byte span rather than node identity, so one analyzer generation's
3736/// re-parses of the same content share the index. A nested `ERROR` that shares
3737/// its parent's span recovers the same classes: the only node the parent adds
3738/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3739#[derive(Default)]
3740pub struct CppRecoveredExportClassIndex {
3741    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3742}
3743
3744impl CppRecoveredExportClassIndex {
3745    pub fn build(root: Node<'_>, source: &str) -> Self {
3746        let mut by_error_node: HashMap<
3747            (usize, usize),
3748            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3749        > = HashMap::default();
3750        // One cursor for the whole-file walk, and the error question asked by
3751        // symbol rather than by `kind()` string (#3097).
3752        let mut cursor = root.walk();
3753        let mut stack = vec![root];
3754        while let Some(node) = stack.pop() {
3755            if node.is_error() {
3756                let recovered = recover_embedded_function_like_export_classes(node, source);
3757                if !recovered.is_empty() {
3758                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3759                }
3760            }
3761            stack.extend(node.named_children(&mut cursor));
3762        }
3763        Self { by_error_node }
3764    }
3765
3766    /// The bytes this index holds, for the analyzer cache's weight.
3767    pub fn approximate_size(&self) -> usize {
3768        self.by_error_node
3769            .values()
3770            .fold(0usize, |total, recovered| {
3771                recovered.iter().fold(
3772                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3773                    |acc, class| {
3774                        acc.saturating_add(std::mem::size_of::<
3775                            RecoveredEmbeddedFunctionLikeExportClass,
3776                        >())
3777                        .saturating_add(class.name.len())
3778                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3779                    },
3780                )
3781            })
3782    }
3783
3784    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3785        self.by_error_node
3786            .get(&(node.start_byte(), node.end_byte()))
3787            .is_some_and(|recovered| {
3788                recovered.iter().any(|class| {
3789                    class.name == identifier
3790                        && class.range.start_byte == range.start_byte
3791                        && class.range.end_byte == range.end_byte
3792                })
3793            })
3794    }
3795}
3796
3797// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3798// `recovered_class_body_at` pops while deciding whether a recovered class shape
3799// claims one declaration range. The count is deterministic for a given source,
3800// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3801// pins it directly instead of timing the walk, the way #2358 pinned the
3802// `remove_code_unit` scan.
3803#[cfg(any(test, feature = "test-support"))]
3804thread_local! {
3805    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3806        const { std::cell::Cell::new(0) };
3807}
3808
3809/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3810/// the calling thread since the last
3811/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3812#[cfg(any(test, feature = "test-support"))]
3813#[doc(hidden)]
3814pub fn recovered_class_body_node_visits_for_test() -> usize {
3815    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3816}
3817
3818/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3819#[cfg(any(test, feature = "test-support"))]
3820#[doc(hidden)]
3821pub fn reset_recovered_class_body_node_visits_for_test() {
3822    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3823}
3824
3825#[cfg(any(test, feature = "test-support"))]
3826fn record_recovered_class_body_visit() {
3827    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3828}
3829
3830#[cfg(not(any(test, feature = "test-support")))]
3831fn record_recovered_class_body_visit() {}
3832
3833/// Whether a recovered class shape named `identifier` owns `range`, and if so
3834/// whether that shape has a body.
3835///
3836/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3837/// forward declaration, and `None` means no recovered shape claims the range,
3838/// so the caller reads the plain `class_specifier` family instead. This is the
3839/// single definition of "does a recovered class have a body": the resolver's
3840/// declaration-strength answer and the navigation occurrence role both read it,
3841/// so an export-macro class is a definition on both paths.
3842///
3843/// The walk follows only the nodes whose span covers `range.start_byte`, which
3844/// is every node that can answer. Each recovered shape reports a range that
3845/// starts at the node's own start byte (the function-like export pair, whose
3846/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3847/// class, which the caller gates on an equal start) or at a token inside the
3848/// node (the embedded export class, keyed on its `class` token, and the
3849/// exported class wrapper, gated on containment here), and every one of them is
3850/// accepted only on an exact match with `range`. Descending everywhere instead
3851/// made one declaration-strength question cost a full pass over the file, so
3852/// asking it once per reference was quadratic in file size: 80% of the 385 s
3853/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3854/// (#1496).
3855pub(crate) fn recovered_class_body_at(
3856    recovered_export_classes: &CppRecoveredExportClassIndex,
3857    root: Node<'_>,
3858    source: &str,
3859    identifier: &str,
3860    range: &Range,
3861) -> Option<bool> {
3862    let covers_range_start = |node: &Node<'_>| {
3863        node.start_byte() <= range.start_byte
3864            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3865    };
3866    let mut stack = vec![root];
3867    let mut saw_forward = false;
3868    while let Some(node) = stack.pop() {
3869        record_recovered_class_body_visit();
3870        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3871        // so an unequal start settles it before the recovery reads the node's
3872        // children at all.
3873        if (node.start_byte() == range.start_byte
3874            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3875            || recovered_export_classes.claims(node, identifier, range)
3876            || (node.start_byte() == range.start_byte
3877                && recovered_fragmented_class_has_body(node, source, identifier, range))
3878        {
3879            return Some(true);
3880        }
3881        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3882            return Some(true);
3883        }
3884        // Macro-decorated exported classes are recovered from a malformed
3885        // function_definition/declaration wrapper. Their indexed class range starts at
3886        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3887        // may also extend the indexed range beyond the wrapper through trailing class
3888        // fragments. Match the structured container that owns the range start by its
3889        // recovered name instead of requiring identical boundaries.
3890        if node.start_byte() <= range.start_byte
3891            && range.start_byte < node.end_byte()
3892            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3893        {
3894            if has_body {
3895                return Some(true);
3896            }
3897            saw_forward = true;
3898            continue;
3899        }
3900        let mut cursor = node.walk();
3901        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3902    }
3903    saw_forward.then_some(false)
3904}
3905
3906/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3907/// head of `identifier`'s member list at `range`.
3908///
3909/// An aggregate whose member list opens with a field-list macro invocation
3910/// never forms a specifier, so the ordinary strength read finds no specifier
3911/// node at the indexed range and answers `Unknown`. A complete definition
3912/// recovered this way then loses the defining-type choice to a forward
3913/// declaration of the same tag in another header, and every member the
3914/// definition declares becomes unreachable through it: libuv's
3915/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3916/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3917/// proves the body, so it answers for its own ranges.
3918fn recovered_collapsed_aggregate_has_body(
3919    node: Node<'_>,
3920    source: &str,
3921    identifier: &str,
3922    range: &Range,
3923) -> bool {
3924    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3925        head.key.start_byte() == range.start_byte
3926            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3927    };
3928    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3929        return true;
3930    }
3931    if !node.is_error() {
3932        return false;
3933    }
3934    let mut cursor = node.walk();
3935    let children = node.children(&mut cursor).collect::<Vec<_>>();
3936    children
3937        .iter()
3938        .enumerate()
3939        .filter(|(_, child)| {
3940            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3941        })
3942        .any(|(index, _)| {
3943            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3944        })
3945}
3946
3947/// Whether `node` is the base type displaced into the declarator field of an
3948/// export-macro class that tree-sitter represented as a declaration or
3949/// function definition.
3950///
3951/// Declaration extraction already recovers this exact malformed envelope as a
3952/// class and records the declarator as its base. Reference extraction must use
3953/// the same structural fact instead of treating the node as a function name.
3954pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3955    if !matches!(
3956        node.kind(),
3957        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3958    ) {
3959        return false;
3960    }
3961    if let Some(function) = node.parent().filter(|parent| {
3962        parent.kind() == "function_definition"
3963            && parent
3964                .child_by_field_name("declarator")
3965                .is_some_and(|declarator| same_node(declarator, node))
3966    }) {
3967        return recover_exported_class_function_definition(function, source)
3968            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3969    }
3970    let Some(initializer) = node.parent().filter(|parent| {
3971        parent.kind() == "init_declarator"
3972            && parent
3973                .child_by_field_name("declarator")
3974                .is_some_and(|declarator| same_node(declarator, node))
3975    }) else {
3976        return false;
3977    };
3978    initializer
3979        .parent()
3980        .filter(|parent| parent.kind() == "declaration")
3981        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3982        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3983}
3984
3985/// Recover the class item from a region reparse that still carries the
3986/// sentinel's synthetic function envelope.  An unknown class attribute can
3987/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3988/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3989/// then nested below that function, so direct class-child lookup is not enough.
3990struct CppSentinelReparsedClass<'tree> {
3991    declaration_node: Node<'tree>,
3992    name: String,
3993    body: Node<'tree>,
3994    raw_supertypes: Option<Vec<String>>,
3995    base_facts: Option<Vec<CppBaseSpecifierFact>>,
3996}
3997
3998fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3999    let mut cursor = root.walk();
4000    root.named_children(&mut cursor)
4001        .find(|child| child.kind() != "comment")
4002        .filter(|child| child.kind() == "template_declaration")
4003}
4004
4005fn cpp_sentinel_reparsed_class<'tree>(
4006    root: Node<'tree>,
4007    template_node: Option<Node<'tree>>,
4008    source: &str,
4009    ancestry: &ParentIndex<'tree>,
4010) -> Option<CppSentinelReparsedClass<'tree>> {
4011    let container = template_node.unwrap_or(root);
4012    let mut cursor = container.walk();
4013    for child in container.named_children(&mut cursor) {
4014        if matches!(
4015            child.kind(),
4016            "class_specifier" | "struct_specifier" | "union_specifier"
4017        ) {
4018            let name = class_like_name(child, source, ancestry)?;
4019            let body = cpp_body_node(child)?;
4020            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4021                .then(|| extract_cpp_supertypes(child, source));
4022            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4023                .then(|| extract_cpp_base_facts(child, source));
4024            return Some(CppSentinelReparsedClass {
4025                declaration_node: child,
4026                name,
4027                body,
4028                raw_supertypes,
4029                base_facts,
4030            });
4031        }
4032        if child.kind() == "declaration"
4033            && let Some(class_node) = first_class_like_child(child)
4034        {
4035            let name = class_like_name(class_node, source, ancestry)?;
4036            let body = cpp_body_node(class_node)?;
4037            let raw_supertypes =
4038                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4039                    .then(|| extract_cpp_supertypes(class_node, source));
4040            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4041                .then(|| extract_cpp_base_facts(class_node, source));
4042            return Some(CppSentinelReparsedClass {
4043                declaration_node: class_node,
4044                name,
4045                body,
4046                raw_supertypes,
4047                base_facts,
4048            });
4049        }
4050        // Only when the nested class item carries its own body. A bodyless
4051        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
4052        // a function definition -- is the export-macro shape recovered by the
4053        // next arm, and must fall through to it rather than abort the search.
4054        if child.kind() == "function_definition"
4055            && let Some(class_node) = first_class_like_child(child)
4056            && let Some(body) = cpp_body_node(class_node)
4057            && let Some(name) = class_like_name(class_node, source, ancestry)
4058        {
4059            let raw_supertypes =
4060                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4061                    .then(|| extract_cpp_supertypes(class_node, source));
4062            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4063                .then(|| extract_cpp_base_facts(class_node, source));
4064            return Some(CppSentinelReparsedClass {
4065                declaration_node: class_node,
4066                name,
4067                body,
4068                raw_supertypes,
4069                base_facts,
4070            });
4071        }
4072        if child.kind() == "function_definition"
4073            && let Some((_, name, raw_supertypes, base_facts)) =
4074                recover_exported_class_function_definition(child, source)
4075        {
4076            let body = cpp_body_node(child)?;
4077            return Some(CppSentinelReparsedClass {
4078                declaration_node: child,
4079                name,
4080                body,
4081                raw_supertypes,
4082                base_facts,
4083            });
4084        }
4085    }
4086    None
4087}
4088
4089fn recovered_postfix_export_macro_base(
4090    node: Node<'_>,
4091    type_node: Node<'_>,
4092    declarator: Node<'_>,
4093    source: &str,
4094) -> Option<String> {
4095    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4096        .and_then(|base| recovered_malformed_base_name(base, source))
4097}
4098
4099fn recovered_postfix_export_macro_base_node<'tree>(
4100    node: Node<'tree>,
4101    type_node: Node<'tree>,
4102    declarator: Node<'tree>,
4103    source: &str,
4104) -> Option<Node<'tree>> {
4105    let mut cursor = node.walk();
4106    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4107        child.kind() == "ERROR"
4108            && child.start_byte() >= type_node.end_byte()
4109            && child.end_byte() <= declarator.start_byte()
4110            && postfix_export_macro_inheritance(*child, source)
4111    });
4112    malformed_clauses.next()?;
4113    if malformed_clauses.next().is_some() {
4114        return None;
4115    }
4116    Some(declarator)
4117}
4118
4119fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4120    let mut macro_count = 0;
4121    let mut colon_count = 0;
4122    let mut access_count = 0;
4123    for index in 0..node.child_count() {
4124        let Some(child) = node.child(index) else {
4125            return false;
4126        };
4127        match child.kind() {
4128            "identifier" | "type_identifier" if child.is_named() => {
4129                let candidate = normalize_cpp_whitespace(node_text(child, source));
4130                if !cpp_export_macro_token(&candidate) {
4131                    return false;
4132                }
4133                macro_count += 1;
4134            }
4135            ":" if !child.is_named() => colon_count += 1,
4136            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4137            _ => return false,
4138        }
4139    }
4140    macro_count == 1 && colon_count == 1 && access_count == 1
4141}
4142
4143fn recovered_single_base_after_declarator_node<'tree>(
4144    node: Node<'tree>,
4145    declarator: Node<'tree>,
4146    source: &str,
4147) -> Option<Node<'tree>> {
4148    let body_start = node
4149        .child_by_field_name("body")
4150        .map(|body| body.start_byte())
4151        .unwrap_or(node.end_byte());
4152    let mut cursor = node.walk();
4153    let mut bases = node
4154        .named_children(&mut cursor)
4155        .filter(|child| {
4156            child.kind() == "ERROR"
4157                && child.start_byte() >= declarator.end_byte()
4158                && child.end_byte() <= body_start
4159        })
4160        .filter_map(|error| {
4161            displaced_exported_class_name(error, source)?;
4162            let mut error_cursor = error.walk();
4163            error
4164                .named_children(&mut error_cursor)
4165                .find(|child| recovered_malformed_base_name(*child, source).is_some())
4166        });
4167    let base = bases.next()?;
4168    bases.next().is_none().then_some(base)
4169}
4170
4171fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4172    (0..node.child_count()).any(|index| {
4173        node.child(index).is_some_and(|child| {
4174            child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4175        })
4176    })
4177}
4178
4179fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4180    matches!(name, "public" | "protected" | "private")
4181}
4182
4183pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4184    recover_exported_class_function_definition(node, source).is_some()
4185}
4186
4187fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4188    matches!(
4189        kind,
4190        "ERROR"
4191            | "preproc_if"
4192            | "preproc_ifdef"
4193            | "preproc_ifndef"
4194            | "preproc_else"
4195            | "preproc_elif"
4196    ) || (kind == "labeled_statement" && in_class_scope)
4197}
4198
4199pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4200    if node.kind() != "declaration" {
4201        return false;
4202    }
4203    let mut ancestor = node.parent();
4204    while let Some(container) = ancestor {
4205        match container.kind() {
4206            "compound_statement" => {
4207                return container.parent().is_some_and(|class_container| {
4208                    is_recovered_exported_class_container(class_container, source)
4209                });
4210            }
4211            // These containers preserve ScopeInfo in visit_node. declaration_list is
4212            // the body container selected for a linkage specification.
4213            "template_declaration" | "linkage_specification" | "declaration_list" => {}
4214            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4215            _ => return false,
4216        }
4217        ancestor = container.parent();
4218    }
4219    false
4220}
4221
4222fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4223    let body_start = node
4224        .child_by_field_name("body")
4225        .map(|body| body.start_byte())
4226        .unwrap_or(node.end_byte());
4227    let mut stack = Vec::new();
4228    for index in (0..node.named_child_count()).rev() {
4229        let Some(child) = node.named_child(index) else {
4230            continue;
4231        };
4232        if child.start_byte() >= body_start {
4233            continue;
4234        }
4235        stack.push(child);
4236    }
4237
4238    let mut best = None;
4239    while let Some(current) = stack.pop() {
4240        if matches!(current.kind(), "identifier" | "type_identifier") {
4241            let name = normalize_cpp_whitespace(node_text(current, source));
4242            if !name.is_empty()
4243                && !cpp_export_macro_token(&name)
4244                && !matches!(name.as_str(), "class" | "struct" | "union")
4245            {
4246                best = Some(name);
4247            }
4248            continue;
4249        }
4250
4251        for index in (0..current.named_child_count()).rev() {
4252            if let Some(child) = current.named_child(index)
4253                && child.start_byte() < body_start
4254            {
4255                stack.push(child);
4256            }
4257        }
4258    }
4259    best
4260}
4261
4262fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4263    if node.kind() == "declaration"
4264        && node
4265            .child_by_field_name("type")
4266            .or_else(|| first_class_like_child(node))
4267            .is_some_and(|type_node| {
4268                matches!(
4269                    type_node.kind(),
4270                    "class_specifier" | "struct_specifier" | "union_specifier"
4271                )
4272            })
4273        && let Some(name) = node
4274            .child_by_field_name("declarator")
4275            .and_then(|declarator| declarator_name_from_node(declarator, source))
4276        && !cpp_export_macro_token(&name)
4277    {
4278        return Some(name);
4279    }
4280
4281    if node.kind() == "function_definition"
4282        && node.child_by_field_name("type").is_some_and(|type_node| {
4283            matches!(
4284                type_node.kind(),
4285                "class_specifier" | "struct_specifier" | "union_specifier"
4286            )
4287        })
4288        && let Some(name) = node
4289            .child_by_field_name("declarator")
4290            .and_then(|declarator| direct_identifier_name(declarator, source))
4291        && !cpp_export_macro_token(&name)
4292    {
4293        return Some(name);
4294    }
4295
4296    let class_node = if matches!(
4297        node.kind(),
4298        "class_specifier" | "struct_specifier" | "union_specifier"
4299    ) {
4300        node
4301    } else {
4302        first_class_like_child(node)?
4303    };
4304    class_like_name_from_children(class_node, source)
4305}
4306
4307/// Normalize the text of a directly selected C++ identifier node.
4308pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4309    if !matches!(
4310        node.kind(),
4311        "identifier" | "field_identifier" | "type_identifier"
4312    ) {
4313        return None;
4314    }
4315    let name = normalize_cpp_whitespace(node_text(node, source));
4316    (!name.is_empty()).then_some(name)
4317}
4318
4319fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4320    if let Some(name) = direct_identifier_name(node, source) {
4321        return Some(name);
4322    }
4323    let mut cursor = node.walk();
4324    node.named_children(&mut cursor)
4325        .find_map(|child| declarator_name_from_node(child, source))
4326}
4327
4328fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4329    let mut cursor = node.walk();
4330    node.named_children(&mut cursor).find(|child| {
4331        matches!(
4332            child.kind(),
4333            "class_specifier" | "struct_specifier" | "union_specifier"
4334        )
4335    })
4336}
4337
4338/// Push a container's children as a `Siblings` cursor rather than snapshotting
4339/// them all with one shared scope: children are visited one at a time so a
4340/// `using namespace X;` sibling can affect the scope threaded to the siblings
4341/// that textually follow it (issue #1093).
4342fn push_cpp_container_work<'tree>(
4343    node: Node<'tree>,
4344    scope: ScopeInfo,
4345    stack: &mut Vec<CppWork<'tree>>,
4346) {
4347    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4348}
4349
4350/// Materialize one selected named-child range with a tree-sitter cursor. The
4351/// cursor advances linearly across the parent's concrete children; repeatedly
4352/// asking for `named_child(index)` is quadratic on very wide generated nodes.
4353fn push_cpp_sibling_range<'tree>(
4354    parent: Node<'tree>,
4355    start_index: usize,
4356    end_index: usize,
4357    scope: ScopeInfo,
4358    stack: &mut Vec<CppWork<'tree>>,
4359) {
4360    let mut cursor = parent.walk();
4361    let children = parent
4362        .named_children(&mut cursor)
4363        .skip(start_index)
4364        .take(end_index.saturating_sub(start_index))
4365        .collect::<Vec<_>>()
4366        .into_iter();
4367    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4368}
4369
4370/// Advance a `Siblings` cursor by one child: dispatch the current child under
4371/// the scope accumulated from its *earlier* siblings, then push a
4372/// continuation for the remaining siblings carrying the scope updated for
4373/// *this* child (only `using namespace X;` directives change it). Pushing the
4374/// continuation before the current child's own node work means the current
4375/// child's subtree fully drains (LIFO) before the next sibling is visited,
4376/// preserving left-to-right order.
4377fn advance_cpp_siblings<'tree>(
4378    mut siblings: CppSiblingsWork<'tree>,
4379    source: &str,
4380    stack: &mut Vec<CppWork<'tree>>,
4381) {
4382    let Some(child) = siblings.children.next() else {
4383        return;
4384    };
4385    let current_scope = siblings.scope.clone();
4386    if let Some(namespace) = cpp_using_namespace_target(child, source) {
4387        siblings.scope.visible_using_namespaces.push(namespace);
4388    }
4389    push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4390    if !siblings.children.as_slice().is_empty() {
4391        stack.push(CppWork::Siblings(siblings));
4392    }
4393    stack.push(CppWork::Node(CppNodeWork {
4394        node: child,
4395        scope: current_scope,
4396    }));
4397}
4398
4399/// The namespace target of a `using namespace X;` directive, or `None` for
4400/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
4401/// kind. Distinguished structurally by the presence of the grammar's literal
4402/// `namespace` keyword token among the node's children -- not by inspecting
4403/// source text -- so it never misreads a member-importing using-declaration
4404/// as a namespace directive.
4405fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4406    if node.kind() != "using_declaration" {
4407        return None;
4408    }
4409    let mut cursor = node.walk();
4410    let is_namespace_directive = node
4411        .children(&mut cursor)
4412        .any(|child| child.kind() == "namespace");
4413    if !is_namespace_directive {
4414        return None;
4415    }
4416    let target = node.named_child(0)?;
4417    // A leading `::` is the explicit-global marker, not part of the namespace
4418    // path (`using namespace ::std::chrono;`). Drop that AST token before
4419    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4420    // keeps: storing the marker verbatim desynced the legacy package string from
4421    // the FqName bridge, which splits on `::` and drops the empty leading
4422    // component, tripping the package/short boundary assert when a bare-owner
4423    // out-of-line definition borrowed the directive's namespace (#1093 path).
4424    let start = target
4425        .child(0)
4426        .filter(|child| !child.is_named() && child.kind() == "::")
4427        .map_or(target.start_byte(), |marker| marker.end_byte());
4428    let text = normalize_cpp_whitespace(
4429        source
4430            .get(start..target.end_byte())
4431            .expect("using-directive target covers one source range"),
4432    );
4433    (!text.is_empty()).then_some(text)
4434}
4435
4436/// Record `node` in `scope` when it is a namespace-scope `using ns::Type;`
4437/// declaration, so later siblings see the binding it introduces.
4438///
4439/// The target is read from the declaration's structured qualified name. A
4440/// using directive (`using namespace ns;`), a `using enum`, a member using
4441/// inside a class body, and a target with a template-id component introduce
4442/// no class binding this extraction can use, so they are skipped. A relative
4443/// target inside a namespace resolves against that namespace, dropping a
4444/// prefix that re-states it, the same reading `split_cpp_name` gives a
4445/// written out-of-line qualifier: a member definition must sit in a namespace
4446/// that encloses its class.
4447fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4448    if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4449        return;
4450    }
4451    let mut cursor = node.walk();
4452    if node
4453        .children(&mut cursor)
4454        .any(|child| matches!(child.kind(), "namespace" | "enum"))
4455    {
4456        return;
4457    }
4458    let Some((components, explicitly_global)) = node
4459        .named_child(0)
4460        .and_then(|target| structured_cpp_qualified_components(target, source))
4461    else {
4462        return;
4463    };
4464    let Some((name, namespace)) = components.split_last() else {
4465        return;
4466    };
4467    if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4468        return;
4469    }
4470    let namespace = namespace
4471        .iter()
4472        .map(|component| component.name.as_str())
4473        .collect::<Vec<_>>();
4474    let target_package = if explicitly_global || scope.package_name.is_empty() {
4475        namespace.join("::")
4476    } else {
4477        let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4478        std::iter::once(scope.package_name.as_str())
4479            .chain(relative.iter().copied())
4480            .collect::<Vec<_>>()
4481            .join("::")
4482    };
4483    Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4484        scope_package: scope.package_name.clone(),
4485        name: name.name.clone(),
4486        target_package,
4487    });
4488}
4489
4490/// Tokens that open a construct only a matching later token closes.
4491const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4492
4493/// The opening tokens of the constructs a C/C++ parse left open at the end of
4494/// the file, in source order (#3501). Empty unless the tree's root is an ERROR
4495/// node.
4496///
4497/// The grammar has no preprocessor, so a construct opened in one conditional
4498/// branch and closed in another stays open: `#if 0` / `extern "C" {` /
4499/// `#endif` leaves its `{` unmatched until a later `#if 0` / `}` / `#endif`.
4500/// One such pair repairs with a single MISSING token. With more of them, the
4501/// parse version tree-sitter keeps reaches the end of input in its error
4502/// state, and tree-sitter wraps every subtree on that version's stack in one
4503/// ERROR node and returns it as the root (`recover_eof` in its `parser.c`).
4504/// The root's children are then exactly that stack: completed subtrees, and
4505/// the bare tokens of the constructs still waiting to be reduced when input
4506/// ran out. A reduced construct is a node of its own, so a bare opener among
4507/// the root's children is one the parse never closed. tdlib/td's copy of the
4508/// SQLite amalgamation parses this way: its root spans all 8,274,268 bytes,
4509/// and its outermost open construct is the `#ifndef SQLITEINT_H` include guard
4510/// at byte 19,718.
4511pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4512    assert!(
4513        root.parent().is_none(),
4514        "only a tree's root records what its parse left open"
4515    );
4516    if !root.is_error() {
4517        return Vec::new();
4518    }
4519    children_iter(root)
4520        .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4521        .collect()
4522}
4523
4524/// Record every declaration of `parsed` that has a range inside `recovery`
4525/// as recovered, unless a narrower recovery already claimed it, and mark the
4526/// reading partial when any declaration lies there (#3501).
4527///
4528/// `recovery` runs from the outermost construct the parse left open to the
4529/// end of the file. Everything before that construct was completed at file
4530/// scope, as in any parse. Everything after it sat on the parse stack above
4531/// an open construct when input ran out, so the scope and nesting the walk
4532/// gave it are a recovery claim rather than a reading of a completed tree,
4533/// and the file's analysis is partial whichever recovery recorded it.
4534fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4535    let recorded: HashSet<CodeUnit> = parsed
4536        .materialization_records
4537        .iter()
4538        .filter_map(|record| match record {
4539            MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4540            _ => None,
4541        })
4542        .collect();
4543    let mut left_open: Vec<(usize, CodeUnit)> = parsed
4544        .declarations()
4545        .iter()
4546        .filter_map(|unit| {
4547            let ranges = parsed.declaration_ranges(unit);
4548            let first = ranges.iter().map(|range| range.start_byte).min()?;
4549            let last = ranges.iter().map(|range| range.start_byte).max()?;
4550            (last >= recovery.start_byte).then(|| (first, unit.clone()))
4551        })
4552        .collect();
4553    if left_open.is_empty() {
4554        return;
4555    }
4556    parsed.completeness = ParseCompleteness::Partial;
4557    left_open.retain(|(_, unit)| !recorded.contains(unit));
4558    left_open.sort();
4559    for (_, unit) in left_open {
4560        parsed
4561            .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4562    }
4563}
4564
4565/// Extract all requested dialect readings from one primary AST event stream.
4566/// Declaration schedulers retain their own admission, recovery and sibling order;
4567/// structural facts, includes and identifiers are interpreted once for the tree.
4568pub(crate) fn parse_cpp_readings<'tree>(
4569    file: &ProjectFile,
4570    source: &str,
4571    root: Node<'tree>,
4572    ancestry: &ParentIndex<'tree>,
4573    dialects: &[LanguageDialect],
4574) -> Vec<ParsedFile> {
4575    parse_cpp_readings_with_object_macro_fields(
4576        file,
4577        source,
4578        root,
4579        ancestry,
4580        dialects,
4581        HashMap::default(),
4582    )
4583}
4584
4585pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4586    file: &ProjectFile,
4587    source: &str,
4588    root: Node<'tree>,
4589    ancestry: &ParentIndex<'tree>,
4590    dialects: &[LanguageDialect],
4591    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4592) -> Vec<ParsedFile> {
4593    assert!(
4594        dialects.len() == 1
4595            || (dialects.len() == 2
4596                && dialects[0] != LanguageDialect::CppC
4597                && dialects[1] == LanguageDialect::CppC),
4598        "one primary dialect, optionally followed by its C alternative"
4599    );
4600    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4601    let properties = RefCell::new(HashMap::default());
4602    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4603    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4604    let mut using_namespaces = Vec::new();
4605    let mut namespace_aliases = Vec::new();
4606    let mut readings: Vec<_> = dialects
4607        .iter()
4608        .map(|_| ParsedFile::new(String::new()))
4609        .collect();
4610    let mut visitors: Vec<_> = readings
4611        .iter_mut()
4612        .zip(dialects)
4613        .map(|(parsed, dialect)| CppVisitor {
4614            file,
4615            source,
4616            parsed,
4617            source_collector: &collector,
4618            source_properties: &properties,
4619            source_dependencies: &dependencies,
4620            primary_ancestry: ancestry,
4621            primary_root_id: root.id(),
4622            source_links: HashMap::default(),
4623            source_declarations: HashSet::default(),
4624            preprocessor_context_cache: Default::default(),
4625            c_tag_semantics: *dialect == LanguageDialect::CppC,
4626            c_tag_scope_witness: false,
4627            recovered_class_sibling_scopes: HashMap::default(),
4628            consumed_fragment_regions: Vec::new(),
4629            orphaned_namespaces: &orphaned_namespaces,
4630            partitioned_regions: Vec::new(),
4631            namespace_forward_scans: HashMap::default(),
4632            recovery_captures: Vec::new(),
4633            active_source_context: None,
4634            object_macro_fields: object_macro_fields.clone(),
4635            ambiguous_object_macro_fields: HashSet::default(),
4636        })
4637        .collect();
4638    let mut declaration_stacks: Vec<_> = dialects
4639        .iter()
4640        .map(|_| {
4641            vec![CppWork::Container(CppContainer {
4642                node: root,
4643                scope: ScopeInfo {
4644                    package_name: String::new(),
4645                    module: None,
4646                    class_unit: None,
4647                    template_signature: None,
4648                    template_metadata: None,
4649                    declarations_are_fields: false,
4650                    recovered_specialization_member_scope: false,
4651                    visible_using_namespaces: Vec::new(),
4652                    visible_using_declarations: Arc::default(),
4653                },
4654            })]
4655        })
4656        .collect();
4657    let kinds = CompiledKinds::compile(
4658        &tree_sitter_cpp::LANGUAGE.into(),
4659        CPP_STRUCTURAL_SPEC.kind_table(),
4660    );
4661    // Forward macro names affect calls before their definitions and require the
4662    // existing bounded preprocessor-only context prepass.
4663    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4664    let mut structural = StructuralFactCollector::new(
4665        &CPP_STRUCTURAL_SPEC,
4666        source,
4667        &context,
4668        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4669        usize::MAX,
4670        None,
4671    );
4672    let mut imports = Vec::new();
4673    let mut includes = Vec::new();
4674    let mut expressions = Vec::new();
4675    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4676    let mut statement_facts = CppStatementFactRows::default();
4677    let mut identifiers = HashSet::default();
4678    let mut entered = HashSet::default();
4679    let mut primary_ordinals = HashMap::default();
4680    let mut stack = vec![(root, None)];
4681    while let Some((node, parent)) = stack.pop() {
4682        assert!(entered.insert(node.id()), "one primary event per AST node");
4683        primary_ordinals.insert(node.id(), primary_ordinals.len());
4684        dependencies.borrow_mut().primary.enter(node, source);
4685        let mut child_parent = parent;
4686        if node.is_named() {
4687            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4688            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4689                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4690            }
4691            if let Some(alias) = crate::source_facts::capture_namespace_alias(
4692                node,
4693                source,
4694                ancestry,
4695                &orphaned_namespaces,
4696                &mut collector.borrow_mut(),
4697            ) {
4698                namespace_aliases.push(alias);
4699            }
4700            if let Some(raw_kind) = kinds.kind_of(&node)
4701                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4702            {
4703                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4704                    node,
4705                    raw_kind,
4706                    parent.map(|id| structural.normalized_kind(id)),
4707                    source,
4708                    &context,
4709                );
4710                let mut source_facts = collector.borrow_mut();
4711                let id = structural
4712                    .enter(node, kind, parent, &mut source_facts)
4713                    .expect("unbounded C++ structural collector");
4714                let mut sink = structural.role_sink(&mut source_facts);
4715                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4716                structural
4717                    .accept_roles(id, sink.into_parts())
4718                    .expect("unbounded C++ structural roles");
4719                child_parent = Some(id);
4720            }
4721            if let Some(fact) = crate::source_facts::capture_expression_fact(
4722                node,
4723                source,
4724                collector.borrow_mut().intern_node(node),
4725                &named_record_sizes,
4726            ) {
4727                expressions.push(fact);
4728            }
4729            if node.kind() == "preproc_include" {
4730                let raw = normalize_cpp_whitespace(node_text(node, source));
4731                if !raw.is_empty() {
4732                    let occurrence = collector.borrow_mut().intern_node(node);
4733                    let target = node.child_by_field_name("path").and_then(|path| {
4734                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4735                        let target = collector.borrow_mut().intern_node(path);
4736                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4737                            declaration: occurrence,
4738                            target,
4739                            path: value.to_owned(),
4740                            quoted,
4741                        });
4742                        Some(target)
4743                    });
4744                    imports.push(SourceImportFact::from_import(
4745                        cpp_include_info(raw),
4746                        occurrence,
4747                        target,
4748                        None,
4749                        Vec::new(),
4750                    ));
4751                }
4752            }
4753            if matches!(
4754                node.kind(),
4755                "type_identifier" | "identifier" | "qualified_identifier"
4756            ) {
4757                let text = node_text(node, source).trim();
4758                if !text.is_empty() {
4759                    identifiers.insert(text.to_owned());
4760                }
4761            }
4762        }
4763        for index in 0..visitors.len() {
4764            // Delay the alternative policy until the primary walk encounters a
4765            // tag whose scope can differ. Its saved declaration stack catches up
4766            // through already-entered events, including recovery, without a
4767            // second AST traversal. A false witness performs no C declaration work.
4768            if index != 0 && !visitors[0].c_tag_scope_witness {
4769                continue;
4770            }
4771            let visitor = &mut visitors[index];
4772            advance_primary_declarations(
4773                visitor,
4774                &mut declaration_stacks[index],
4775                &entered,
4776                ancestry,
4777            );
4778            visitor.drain_partitioned_regions();
4779        }
4780        let mut cursor = node.walk();
4781        let children: Vec<_> = node
4782            .children(&mut cursor)
4783            .map(|child| (child, child_parent))
4784            .collect();
4785        stack.extend(children.into_iter().rev());
4786    }
4787    let reading_count = if visitors[0].c_tag_scope_witness {
4788        visitors.len()
4789    } else {
4790        1
4791    };
4792    visitors.truncate(reading_count);
4793    declaration_stacks.truncate(reading_count);
4794    assert!(
4795        declaration_stacks.iter().all(Vec::is_empty),
4796        "all primary declaration events were consumed"
4797    );
4798    for visitor in &mut visitors {
4799        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4800            .into_iter()
4801            .flat_map(|(unit, declarations)| {
4802                declarations
4803                    .into_iter()
4804                    .map(move |declaration| (declaration, unit.clone()))
4805            })
4806            .collect();
4807    }
4808    let dialect_declarations: Vec<_> = visitors
4809        .iter()
4810        .map(|visitor| visitor.source_declarations.clone())
4811        .collect();
4812    drop(visitors);
4813    readings.truncate(reading_count);
4814    dependencies
4815        .borrow_mut()
4816        .finish_primary(&mut properties.borrow_mut());
4817    // Preserve the existing malformed quoted-directive recovery, but publish its
4818    // explicit source occurrence into the same import family as parsed directives.
4819    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4820    let primary_occurrence_ordinals: HashMap<_, _> = collector
4821        .borrow()
4822        .primary_node_occurrences()
4823        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4824        .collect();
4825    let occurrences = collector.into_inner().finish();
4826    let generic_imports: Vec<_> = (0..imports.len())
4827        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4828        .collect();
4829    let source_facts = ParsedSourceFacts {
4830        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4831            includes,
4832            declarations: {
4833                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4834                facts.sort_by_key(|fact| fact.declaration.get());
4835                facts
4836            },
4837            using_namespaces,
4838            namespace_aliases,
4839            expressions,
4840            statement_facts,
4841        }),
4842        go: None,
4843        java: None,
4844        js_ts: None,
4845        php: None,
4846        scala: None,
4847        ruby: None,
4848        python: None,
4849        source_bytes: source.len(),
4850        occurrences,
4851        structural: structural
4852            .finish(root)
4853            .expect("C++ structural collection finishes"),
4854        native_site_occurrences: Vec::new(),
4855        native_declaration_sources: Vec::new(),
4856        declaration_visibilities: None,
4857        rust_declaration_properties: Vec::new(),
4858        rust_modules: None,
4859        rust_types: Vec::new(),
4860        rust_items: Default::default(),
4861        imports,
4862        generic_imports,
4863        rust_import_contexts: Vec::new(),
4864    };
4865    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4866        parsed.finalize_deferred_replacements();
4867        let links = std::mem::take(&mut parsed.source_declaration_units);
4868        parsed.source_declaration_units = links
4869            .into_iter()
4870            .filter(|(_, unit)| parsed.contains_declaration(unit))
4871            .collect();
4872        parsed.imports = source_facts
4873            .imports
4874            .iter()
4875            .map(|import| import.import_info(&source_facts.occurrences))
4876            .collect();
4877        parsed.type_identifiers = identifiers.clone();
4878        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4879            &source_facts,
4880            &declarations,
4881            &primary_occurrence_ordinals,
4882            &mut parsed.source_declaration_units,
4883            &mut parsed.source_declaration_metadata,
4884        ));
4885    }
4886    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4887        // The recovery runs to the end of the file, which is how a reader of
4888        // the record tells this recovery from a local one (#3501).
4889        let recovery = Range {
4890            start_byte: outermost.start_byte(),
4891            end_byte: source.len(),
4892            start_line: outermost.start_position().row + 1,
4893            end_line: root.end_position().row + 1,
4894        };
4895        for parsed in &mut readings {
4896            record_declarations_left_open(parsed, recovery);
4897        }
4898    }
4899    readings
4900}
4901
4902fn advance_primary_declarations<'tree>(
4903    visitor: &mut CppVisitor<'_, '_>,
4904    stack: &mut Vec<CppWork<'tree>>,
4905    entered: &HashSet<usize>,
4906    ancestry: &ParentIndex<'tree>,
4907) {
4908    while let Some(work) = stack.pop() {
4909        match work {
4910            CppWork::Container(container) => {
4911                push_cpp_container_work(container.node, container.scope, stack)
4912            }
4913            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4914            CppWork::Node(work) => {
4915                if !entered.contains(&work.node.id()) {
4916                    stack.push(CppWork::Node(work));
4917                    return;
4918                }
4919                if !visitor.node_is_inside_consumed_fragment(work.node) {
4920                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4921                }
4922            }
4923        }
4924    }
4925}
4926
4927#[derive(Clone)]
4928struct CppActiveSourceContext {
4929    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4930    namespace: Option<String>,
4931    child_namespace: Option<String>,
4932    outer_lexical_scope: Vec<String>,
4933    child_lexical_scope: Vec<String>,
4934    family: Option<SourceOccurrenceId>,
4935    exhaustive_family: Option<SourceOccurrenceId>,
4936    callable_guard_completion_byte: Option<usize>,
4937}
4938
4939/// One owned container reparse deferred until the primary declaration walk has
4940/// reached its source position. The tree and context are owned so the deferred
4941/// walk never retains a `Node` borrowed from a temporary recovery tree.
4942pub struct CppPartitionedRegion {
4943    tree: Tree,
4944    range: std::ops::Range<usize>,
4945    scope: ScopeInfo,
4946    recovery: Range,
4947    source_context: Option<CppActiveSourceContext>,
4948}
4949
4950pub struct CppVisitor<'a, 'source> {
4951    pub file: &'a ProjectFile,
4952    pub source: &'source str,
4953    pub parsed: &'a mut ParsedFile,
4954    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4955    source_properties: &'a RefCell<
4956        HashMap<
4957            SourceDeclarationId,
4958            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4959        >,
4960    >,
4961    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4962    pub primary_ancestry: &'a ParentIndex<'a>,
4963    pub primary_root_id: usize,
4964    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4965    source_declarations: HashSet<SourceDeclarationId>,
4966    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
4967    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
4968    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
4969    ///
4970    /// C has no nested tag scope: a struct/union/enum tag declared inside
4971    /// another aggregate's member list has the scope of the outer declaration
4972    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
4973    /// therefore declares a file-scope `inner` that a later file-scope
4974    /// `struct inner *p;` legitimately references, where C++ would make the
4975    /// same shape a nested class `outer::inner`. Headers carry no compilation
4976    /// language of their own and keep the conservative C++ interpretation.
4977    pub c_tag_semantics: bool,
4978    /// Sticky evidence that this C++ walk reached a declaration whose scope
4979    /// would change under C tag semantics, including visitor recovery work.
4980    pub(crate) c_tag_scope_witness: bool,
4981    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4982    /// Byte regions whose contents were re-owned by a fragmented export-class
4983    /// recovery (#938): the scattered members between the fragmented
4984    /// declaration and its displaced closing brace are indexed as members of
4985    /// the recovered class by the region reparse, so the ordinary sibling walk
4986    /// must not ALSO index them as top-level declarations (that double-indexing
4987    /// made a scattered nested class ambiguous between `Inner` and
4988    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
4989    /// linear scan at visit time is fine.
4990    pub consumed_fragment_regions: Vec<(usize, usize)>,
4991    /// Lexical namespace corrections and brace boundaries for declarations
4992    /// displaced by parse recovery (issues #1537 and #3087). The parsed
4993    /// ancestors can stop short or extend past their true closes; see
4994    /// [`CppVisitor::recovered_namespace_scope`].
4995    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4996    /// Owned reparses waiting for the outer work loop. Partitioning another
4997    /// swallowed class in a tail must not grow the Rust call stack.
4998    pub partitioned_regions: Vec<CppPartitionedRegion>,
4999    /// The namespace forward declarations already folded out of each tree this
5000    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
5001    /// Empty until the first question, which the overwhelming majority of files
5002    /// never ask.
5003    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5004    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
5005    /// happen to the declaration set, innermost last. Empty outside a recovery
5006    /// reparse, which is almost always (#2787).
5007    pub recovery_captures: Vec<CppRecoveryCapture>,
5008    active_source_context: Option<CppActiveSourceContext>,
5009    /// Object-like field-list macros defined earlier in this source. Their
5010    /// replacements are parsed structurally and materialized under each
5011    /// invoking aggregate; no source-text expansion is used.
5012    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5013    /// Names whose active replacement is not a unique structured field list.
5014    /// A later `#undef` resets the ambiguity; another `#define` does not.
5015    pub ambiguous_object_macro_fields: HashSet<String>,
5016}
5017
5018/// One source-order event from an object-like field-list macro environment.
5019///
5020/// The event stream lets an include-closure consumer preserve preprocessor
5021/// invalidation without guessing which parsed header happened to be visited
5022/// first. A conservative consumer may permanently block a name after an
5023/// `undef` when conditional include order is not provable.
5024#[derive(Clone, Debug, PartialEq, Eq)]
5025pub enum ObjectMacroFieldEvent {
5026    Define {
5027        name: String,
5028        replacement: ObjectMacroReplacement,
5029        conditional: bool,
5030    },
5031    Undef {
5032        name: String,
5033        conditional: bool,
5034    },
5035}
5036
5037/// Collect object-like field-list macro events in source order. Conditional
5038/// branches remain visible in the event stream: callers that cannot prove
5039/// branch selection can invalidate names conservatively instead of inventing
5040/// a replacement from one branch.
5041pub fn collect_cpp_object_macro_field_events<'tree>(
5042    root: Node<'tree>,
5043    source: &str,
5044) -> Vec<ObjectMacroFieldEvent> {
5045    let mut events = Vec::new();
5046    let mut stack = vec![root];
5047    while let Some(node) = stack.pop() {
5048        if node.kind() == "preproc_def"
5049            && let Some(name) = extract_macro_name(node, source)
5050        {
5051            let replacement = object_macro_replacement_of(node, source);
5052            events.push(ObjectMacroFieldEvent::Define {
5053                name,
5054                replacement,
5055                conditional: inside_preprocessor_conditional(node),
5056            });
5057        } else if is_cpp_undef_directive(node, source)
5058            && let Some(argument) = node.child_by_field_name("argument")
5059        {
5060            events.push(ObjectMacroFieldEvent::Undef {
5061                name: node_text(argument, source).trim().to_string(),
5062                conditional: inside_preprocessor_conditional(node),
5063            });
5064        }
5065        let mut cursor = node.walk();
5066        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5067        stack.extend(children.into_iter().rev());
5068    }
5069    events
5070}
5071
5072fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5073    let mut current = node.parent();
5074    while let Some(parent) = current {
5075        if is_cpp_preprocessor_conditional(parent) {
5076            return true;
5077        }
5078        current = parent.parent();
5079    }
5080    false
5081}
5082
5083fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5084    node.kind() == "preproc_call"
5085        && node
5086            .child_by_field_name("directive")
5087            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5088}
5089
5090impl<'a, 'source> CppVisitor<'a, 'source> {
5091    fn apply_active_source_context(
5092        &self,
5093        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5094    ) {
5095        let Some(context) = self.active_source_context.as_ref() else {
5096            return;
5097        };
5098        fact.guard_requirements = fact
5099            .guard_requirements
5100            .as_ref()
5101            .and_then(crate::source_context::cpp_guard_set_to_runtime)
5102            .zip(context.guards.as_ref())
5103            .and_then(|(local, outer)| {
5104                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5105            })
5106            .as_ref()
5107            .map(crate::source_context::cpp_guard_set_from_runtime);
5108        if fact.callable_guards.is_some() {
5109            fact.callable_guards = fact
5110                .callable_guards
5111                .as_ref()
5112                .and_then(crate::source_context::cpp_guard_set_to_runtime)
5113                .zip(context.guards.as_ref())
5114                .and_then(|(local, outer)| {
5115                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5116                })
5117                .as_ref()
5118                .map(crate::source_context::cpp_guard_set_from_runtime);
5119        }
5120        fact.callable_guard_completion_byte = fact
5121            .callable_guard_completion_byte
5122            .zip(context.callable_guard_completion_byte)
5123            .map(|(local, outer)| local.max(outer));
5124        fact.conditional_family = fact.conditional_family.or(context.family);
5125        fact.exhaustive_conditional_family = fact
5126            .exhaustive_conditional_family
5127            .or(context.exhaustive_family);
5128        if let Some(alias) = fact.file_scope_alias.as_mut() {
5129            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5130                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5131                (Some(outer), None) => Some(outer.clone()),
5132                (None, local) => local,
5133            };
5134        }
5135        if !context.outer_lexical_scope.is_empty() {
5136            let mut lexical = context.outer_lexical_scope.clone();
5137            lexical.append(&mut fact.lexical_path);
5138            fact.lexical_path = lexical;
5139        }
5140    }
5141
5142    fn recovery_ancestry<'tree>(
5143        &self,
5144        root: Node<'tree>,
5145    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5146        crate::source_dependencies::RecoveryAncestry::new(
5147            root,
5148            self.source,
5149            self.source_dependencies,
5150        )
5151    }
5152
5153    fn capture_source_dependencies(
5154        &self,
5155        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5156        node: Node<'_>,
5157        unit: &CodeUnit,
5158    ) {
5159        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5160            self.source_dependencies.borrow_mut().register_primary(
5161                fact.declaration,
5162                node,
5163                unit.is_class(),
5164            );
5165        } else {
5166            self.source_dependencies
5167                .borrow()
5168                .apply_recovered(node, unit.is_class(), fact);
5169        }
5170    }
5171
5172    fn link_source_declaration(
5173        &mut self,
5174        ancestry: &ParentIndex<'_>,
5175        unit: &CodeUnit,
5176        node: Node<'_>,
5177    ) {
5178        let (declaration, family) = {
5179            let mut collector = self.source_collector.borrow_mut();
5180            let occurrence = if node.id() == self.primary_root_id
5181                || self.primary_ancestry.contains(node)
5182            {
5183                collector.intern_node(node)
5184            } else {
5185                // A region reparse owns distinct provenance even when its byte range
5186                // equals a primary node or a previously dropped recovery tree.
5187                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5188            };
5189            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5190                if self.primary_ancestry.contains(name) {
5191                    collector.intern_node(name)
5192                } else {
5193                    collector
5194                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5195                }
5196            });
5197            let declaration = collector.declare(occurrence, name);
5198            let family =
5199                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5200                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5201                    {
5202                        collector.intern_node(family)
5203                    } else {
5204                        collector.intern_embedded(
5205                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5206                        )
5207                    }
5208                });
5209            (declaration, family)
5210        };
5211        self.source_declarations.insert(declaration);
5212        if !self.source_properties.borrow().contains_key(&declaration) {
5213            let context = crate::source_context::capture_declaration_context(
5214                node,
5215                unit,
5216                self.source,
5217                &mut self.preprocessor_context_cache,
5218            );
5219            let (exhaustive_family, displaced_namespace_closing_brace) = {
5220                let mut collector = self.source_collector.borrow_mut();
5221                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5222                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5223                    {
5224                        collector.intern_node(family)
5225                    } else {
5226                        collector.intern_embedded(
5227                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5228                        )
5229                    }
5230                });
5231                let displaced_namespace_closing_brace =
5232                    context.displaced_namespace_closing_brace.map(|brace| {
5233                        if brace.id() == self.primary_root_id
5234                            || self.primary_ancestry.contains(brace)
5235                        {
5236                            collector.intern_node(brace)
5237                        } else {
5238                            collector.intern_embedded(
5239                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5240                            )
5241                        }
5242                    });
5243                (exhaustive_family, displaced_namespace_closing_brace)
5244            };
5245            let guard_requirements = context.guard_requirements;
5246            let callable_guards = context.callable_guards;
5247            let callable_activation = context.callable_activation;
5248            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5249            let flattened_macro_namespace = context.flattened_macro_namespace;
5250            self.source_properties
5251                .borrow_mut()
5252                .entry(declaration)
5253                .or_insert_with(|| {
5254                    let mut fact = crate::source_facts::capture_declaration(
5255                        declaration,
5256                        node,
5257                        unit,
5258                        self.source,
5259                        ancestry,
5260                    );
5261                    self.capture_source_dependencies(&mut fact, node, unit);
5262                    fact.occurrence_role =
5263                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5264                    fact.conditional_family = family;
5265                    fact.guard_requirements = guard_requirements;
5266                    fact.callable_guards = callable_guards;
5267                    fact.callable_activation = callable_activation;
5268                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
5269                    fact.exhaustive_conditional_family = exhaustive_family;
5270                    fact.flattened_macro_namespace = flattened_macro_namespace;
5271                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5272                    self.apply_active_source_context(&mut fact);
5273                    fact
5274                });
5275        }
5276        let links = self.source_links.entry(unit.clone()).or_default();
5277        if !links.contains(&declaration) {
5278            links.push(declaration);
5279        }
5280    }
5281
5282    fn link_source_range(
5283        &mut self,
5284        ancestry: &ParentIndex<'_>,
5285        unit: &CodeUnit,
5286        range: Range,
5287        evidence: Option<Node<'_>>,
5288    ) {
5289        let declaration = {
5290            let mut collector = self.source_collector.borrow_mut();
5291            let occurrence =
5292                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5293            let name = evidence
5294                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5295                .map(|name| {
5296                    if self.primary_ancestry.contains(name) {
5297                        collector.intern_node(name)
5298                    } else {
5299                        collector.intern_embedded(
5300                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5301                        )
5302                    }
5303                });
5304            collector.declare(occurrence, name)
5305        };
5306        self.link_source_declaration_facts(
5307            unit,
5308            declaration,
5309            evidence.map(|node| (ancestry, node)),
5310        );
5311    }
5312
5313    /// [`Self::link_source_range`] with the name occurrence interned from the
5314    /// subspan that writes it instead of derived from an evidence node. A live
5315    /// evidence node still supplies the declaration's source facts.
5316    fn link_source_name_range(
5317        &mut self,
5318        unit: &CodeUnit,
5319        range: Range,
5320        name_range: Range,
5321        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5322    ) {
5323        let declaration = {
5324            let mut collector = self.source_collector.borrow_mut();
5325            let occurrence =
5326                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5327            let name =
5328                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5329            collector.declare(occurrence, Some(name))
5330        };
5331        self.link_source_declaration_facts(unit, declaration, evidence);
5332    }
5333
5334    /// Record `declaration` for `unit` and, when a live evidence node exists,
5335    /// the source facts it supplies: conditional family, guards, context and
5336    /// dependency names. Shared by both occurrence-naming paths so a name
5337    /// subspan never costs a declaration its facts (#3093).
5338    fn link_source_declaration_facts(
5339        &mut self,
5340        unit: &CodeUnit,
5341        declaration: SourceDeclarationId,
5342        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5343    ) {
5344        let family = evidence.and_then(|(_, node)| {
5345            let mut collector = self.source_collector.borrow_mut();
5346            crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5347                if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5348                    collector.intern_node(family)
5349                } else {
5350                    collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5351                        family,
5352                    ))
5353                }
5354            })
5355        });
5356        self.source_declarations.insert(declaration);
5357        if let Some((ancestry, node)) = evidence
5358            && !self.source_properties.borrow().contains_key(&declaration)
5359        {
5360            let context = crate::source_context::capture_declaration_context(
5361                node,
5362                unit,
5363                self.source,
5364                &mut self.preprocessor_context_cache,
5365            );
5366            let (exhaustive_family, displaced_namespace_closing_brace) = {
5367                let mut collector = self.source_collector.borrow_mut();
5368                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5369                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5370                    {
5371                        collector.intern_node(family)
5372                    } else {
5373                        collector.intern_embedded(
5374                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5375                        )
5376                    }
5377                });
5378                let displaced_namespace_closing_brace =
5379                    context.displaced_namespace_closing_brace.map(|brace| {
5380                        if brace.id() == self.primary_root_id
5381                            || self.primary_ancestry.contains(brace)
5382                        {
5383                            collector.intern_node(brace)
5384                        } else {
5385                            collector.intern_embedded(
5386                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5387                            )
5388                        }
5389                    });
5390                (exhaustive_family, displaced_namespace_closing_brace)
5391            };
5392            let guard_requirements = context.guard_requirements;
5393            let callable_guards = context.callable_guards;
5394            let callable_activation = context.callable_activation;
5395            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5396            let flattened_macro_namespace = context.flattened_macro_namespace;
5397            let mut fact = crate::source_facts::capture_declaration(
5398                declaration,
5399                node,
5400                unit,
5401                self.source,
5402                ancestry,
5403            );
5404            self.capture_source_dependencies(&mut fact, node, unit);
5405            fact.occurrence_role =
5406                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5407            fact.conditional_family = family;
5408            fact.guard_requirements = guard_requirements;
5409            fact.callable_guards = callable_guards;
5410            fact.callable_activation = callable_activation;
5411            fact.callable_guard_completion_byte = callable_guard_completion_byte;
5412            fact.exhaustive_conditional_family = exhaustive_family;
5413            fact.flattened_macro_namespace = flattened_macro_namespace;
5414            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5415            self.apply_active_source_context(&mut fact);
5416            self.source_properties
5417                .borrow_mut()
5418                .insert(declaration, fact);
5419        }
5420        let links = self.source_links.entry(unit.clone()).or_default();
5421        if !links.contains(&declaration) {
5422            links.push(declaration);
5423        }
5424    }
5425
5426    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5427        let declaration = *self
5428            .source_links
5429            .get(&unit)
5430            .and_then(|links| links.last())
5431            .expect("signature metadata follows its source declaration");
5432        let metadata_ordinal = self
5433            .parsed
5434            .add_signature_with_metadata(unit.clone(), metadata);
5435        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5436            declaration,
5437            unit,
5438            metadata_ordinal,
5439        };
5440        if !self.parsed.source_declaration_metadata.contains(&link) {
5441            self.parsed.source_declaration_metadata.push(link);
5442        }
5443    }
5444
5445    /// Records `code_unit` with the answers the walk carries forward, then adds
5446    /// it to the parse product.
5447    ///
5448    /// Every declaration this walk publishes goes through this family, so the
5449    /// carried-forward answers see each one exactly once: the minted declaration
5450    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5451    fn add_declaration(
5452        &mut self,
5453        ancestry: &ParentIndex<'_>,
5454        code_unit: CodeUnit,
5455        node: Node<'_>,
5456        parent: Option<CodeUnit>,
5457        top_level: Option<CodeUnit>,
5458    ) {
5459        self.note_declaration(&code_unit);
5460        self.link_source_declaration(ancestry, &code_unit, node);
5461        let source = self.source;
5462        self.parsed
5463            .add_code_unit(code_unit, node, source, parent, top_level);
5464    }
5465
5466    /// Range-based form of [`Self::add_declaration`].
5467    fn add_declaration_with_range(
5468        &mut self,
5469        ancestry: &ParentIndex<'_>,
5470        code_unit: CodeUnit,
5471        range: Range,
5472        evidence: Option<Node<'_>>,
5473        parent: Option<CodeUnit>,
5474        top_level: Option<CodeUnit>,
5475    ) {
5476        self.note_declaration(&code_unit);
5477        self.link_source_range(ancestry, &code_unit, range, evidence);
5478        self.parsed
5479            .add_code_unit_with_range(code_unit, range, parent, top_level);
5480    }
5481
5482    /// Name-subspan form of [`Self::add_declaration_with_range`]: the name
5483    /// occurrence is the recorded subspan rather than a token derived from an
5484    /// evidence node (#4292). A live `evidence` node, when the caller has one,
5485    /// still supplies the declaration's source facts; an index-recovered
5486    /// declaration has none.
5487    fn add_declaration_with_name_range(
5488        &mut self,
5489        code_unit: CodeUnit,
5490        range: Range,
5491        name_range: Range,
5492        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5493        parent: Option<CodeUnit>,
5494        top_level: Option<CodeUnit>,
5495    ) {
5496        self.note_declaration(&code_unit);
5497        self.link_source_name_range(&code_unit, range, name_range, evidence);
5498        self.parsed
5499            .add_code_unit_with_range(code_unit, range, parent, top_level);
5500    }
5501
5502    /// Deferred-replacement form of [`Self::add_declaration`].
5503    fn replace_declaration_deferred(
5504        &mut self,
5505        ancestry: &ParentIndex<'_>,
5506        code_unit: CodeUnit,
5507        node: Node<'_>,
5508        parent: Option<CodeUnit>,
5509        top_level: Option<CodeUnit>,
5510    ) {
5511        self.note_replaced_declaration(&code_unit);
5512        self.link_source_declaration(ancestry, &code_unit, node);
5513        let source = self.source;
5514        self.parsed
5515            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5516    }
5517
5518    /// Range-based form of [`Self::replace_declaration_deferred`].
5519    fn replace_declaration_with_range_deferred(
5520        &mut self,
5521        ancestry: &ParentIndex<'_>,
5522        code_unit: CodeUnit,
5523        range: Range,
5524        evidence: Option<Node<'_>>,
5525        parent: Option<CodeUnit>,
5526        top_level: Option<CodeUnit>,
5527    ) {
5528        self.note_replaced_declaration(&code_unit);
5529        self.link_source_range(ancestry, &code_unit, range, evidence);
5530        self.parsed
5531            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5532    }
5533
5534    /// Notes one declaration about to enter the parse product.
5535    ///
5536    /// A declaration the product already holds is not a creation, so an open
5537    /// recovery capture ignores it -- which is the membership test the set
5538    /// difference it replaces performed. A creation inside a nested recovery
5539    /// belongs to the recoveries around it too, so every open capture takes it.
5540    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5541        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5542            for capture in &mut self.recovery_captures {
5543                if capture.removed_pre_existing.contains(code_unit) {
5544                    continue;
5545                }
5546                if capture.created_units.insert(code_unit.clone()) {
5547                    capture.created.push(code_unit.clone());
5548                }
5549            }
5550        }
5551    }
5552
5553    /// Notes one declaration about to replace an existing one.
5554    ///
5555    /// A deferred replacement of a declaration that already owns children
5556    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5557    /// their source mounts and recovery captures must track the removal.
5558    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5559        // Forward and definition occurrences remain real navigation alternatives.
5560        // Only identities removed with replaced children lose their mounts.
5561        for removed in self.declarations_a_replacement_removes(code_unit) {
5562            self.source_links.remove(&removed);
5563        }
5564        let removes_children = self.parsed.contains_declaration(code_unit)
5565            && self
5566                .parsed
5567                .children
5568                .get(code_unit)
5569                .is_some_and(|children| !children.is_empty());
5570        if removes_children && !self.recovery_captures.is_empty() {
5571            let removed = self.declarations_a_replacement_removes(code_unit);
5572            for capture in &mut self.recovery_captures {
5573                for unit in &removed {
5574                    // A declaration this capture watched being created is
5575                    // its own; one it did not is a declaration that was
5576                    // already there when the capture opened, so creating it
5577                    // again is a restoration and not a mint.
5578                    if !capture.created_units.contains(unit) {
5579                        capture.removed_pre_existing.insert(unit.clone());
5580                    }
5581                }
5582            }
5583        }
5584        self.note_declaration(code_unit);
5585    }
5586
5587    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5588    /// when `code_unit` is replaced: its children, transitively.
5589    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5590        let mut removed = Vec::new();
5591        let mut seen = HashSet::default();
5592        let mut pending: Vec<CodeUnit> = self
5593            .parsed
5594            .children
5595            .get(code_unit)
5596            .cloned()
5597            .unwrap_or_default();
5598        while let Some(unit) = pending.pop() {
5599            if !seen.insert(unit.clone()) {
5600                continue;
5601            }
5602            if let Some(children) = self.parsed.children.get(&unit) {
5603                pending.extend(children.iter().cloned());
5604            }
5605            removed.push(unit);
5606        }
5607        removed
5608    }
5609
5610    fn visit_recovered_function_like_export_class<'tree>(
5611        &mut self,
5612        node: Node<'tree>,
5613        recovered: RecoveredFunctionLikeExportClass,
5614        scope: &ScopeInfo,
5615        stack: &mut Vec<CppWork<'tree>>,
5616        ancestry: &ParentIndex<'tree>,
5617    ) {
5618        let RecoveredFunctionLikeExportClass {
5619            name,
5620            range,
5621            raw_supertypes,
5622            base_facts,
5623            member_outcome,
5624        } = recovered;
5625        let class_unit = self.visit_named_class_like_shape(
5626            node,
5627            name,
5628            None,
5629            true,
5630            Some(range),
5631            raw_supertypes,
5632            base_facts,
5633            scope,
5634            stack,
5635            ancestry,
5636        );
5637        self.parsed
5638            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5639                recovery: range,
5640                unit: class_unit.clone(),
5641            });
5642        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5643            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5644                tree.root_node(),
5645                class_unit.identifier(),
5646                self.source,
5647            )
5648        {
5649            self.visit_recovered_fragment_constructor(
5650                constructor_range,
5651                body,
5652                node,
5653                &class_unit,
5654                scope,
5655                ancestry,
5656            );
5657        }
5658        if let Some(outcome) = member_outcome {
5659            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5660        }
5661    }
5662
5663    fn visit_function_like_export_class_pair<'tree>(
5664        &mut self,
5665        node: Node<'tree>,
5666        scope: &ScopeInfo,
5667        stack: &mut Vec<CppWork<'tree>>,
5668        ancestry: &ParentIndex<'tree>,
5669    ) -> bool {
5670        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5671            return false;
5672        };
5673        let member_outcome = self
5674            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5675        // A malformed class body can escape into several following siblings
5676        // before the next export-macro class head appears. Inspect siblings in
5677        // order and stop at the first envelope that contains such a head. One
5678        // envelope can contain several following classes, all recovered in a
5679        // single bounded traversal.
5680        let mut displaced = node.next_named_sibling();
5681        while let Some(candidate) = displaced {
5682            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5683                break;
5684            }
5685            displaced = candidate.next_named_sibling();
5686        }
5687        // The adjacent initializer_list proves the class body envelope, but
5688        // its children are expression-shaped rather than declaration-shaped.
5689        // Visit only separately recovered members; ordinary callable
5690        // definitions remain available from their out-of-line declarations.
5691        let range = recovered.range;
5692        self.visit_recovered_function_like_export_class(
5693            node,
5694            RecoveredFunctionLikeExportClass {
5695                name: recovered.name,
5696                range,
5697                raw_supertypes: recovered.raw_supertypes,
5698                base_facts: recovered.base_facts,
5699                member_outcome,
5700            },
5701            scope,
5702            stack,
5703            ancestry,
5704        );
5705        self.consumed_fragment_regions
5706            .push((node.start_byte(), range.end_byte));
5707        true
5708    }
5709
5710    fn visit_embedded_function_like_export_classes<'tree>(
5711        &mut self,
5712        node: Node<'tree>,
5713        scope: &ScopeInfo,
5714        stack: &mut Vec<CppWork<'tree>>,
5715        ancestry: &ParentIndex<'tree>,
5716    ) -> bool {
5717        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5718        let found = !recovered_classes.is_empty();
5719        for recovered in recovered_classes {
5720            let member_outcome = self.reparse_fragmented_export_class_members(
5721                &recovered.fragmented_body,
5722                &recovered.name,
5723            );
5724            self.visit_recovered_function_like_export_class(
5725                node,
5726                RecoveredFunctionLikeExportClass {
5727                    name: recovered.name,
5728                    range: recovered.range,
5729                    raw_supertypes: Some(recovered.raw_supertypes),
5730                    base_facts: Some(recovered.base_facts),
5731                    member_outcome,
5732                },
5733                scope,
5734                stack,
5735                ancestry,
5736            );
5737        }
5738        found
5739    }
5740
5741    /// Walk `node`'s container, answering every ancestor question from
5742    /// `ancestry`.
5743    ///
5744    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5745    /// because one tree can be walked more than once -- a header's C and C++
5746    /// readings are the same tree under different tag semantics -- and the
5747    /// parent relation is a property of the tree, not of the reading.
5748    #[allow(clippy::too_many_arguments)]
5749    pub fn visit_container<'tree>(
5750        &mut self,
5751        node: Node<'tree>,
5752        ancestry: &ParentIndex<'tree>,
5753        package_name: &str,
5754        module: Option<CodeUnit>,
5755        class_unit: Option<CodeUnit>,
5756        template_signature: Option<String>,
5757        visible_using_namespaces: Vec<String>,
5758        visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5759    ) {
5760        let scope = ScopeInfo {
5761            package_name: package_name.to_string(),
5762            module,
5763            class_unit,
5764            template_signature,
5765            template_metadata: None,
5766            declarations_are_fields: false,
5767            recovered_specialization_member_scope: false,
5768            visible_using_namespaces,
5769            visible_using_declarations,
5770        };
5771        // The work loop dispatches a container's children, never the container
5772        // itself, so an `ERROR` container that is the seed does not reach the
5773        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5774        // breaks the parse leaves the whole translation unit as that seed
5775        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5776        // macro-error-class recovery reads.
5777        if node.is_error() {
5778            self.visit_object_macro_error_classes(node, &scope);
5779        }
5780        self.run_container_work(node, scope, ancestry);
5781        self.drain_partitioned_regions();
5782    }
5783
5784    /// Drain reparsed containers after their primary walk has indexed them.
5785    /// Recovered nodes retain their own tree and source context while they are
5786    /// visited; only then are their byte ranges marked consumed so a shared
5787    /// primary event stream does not visit the same declarations twice.
5788    fn drain_partitioned_regions(&mut self) {
5789        let mut consumed = Vec::new();
5790        while let Some(CppPartitionedRegion {
5791            tree,
5792            range,
5793            scope,
5794            recovery,
5795            source_context,
5796        }) = self.partitioned_regions.pop()
5797        {
5798            let root = tree.root_node();
5799            let container = root
5800                .descendant_for_byte_range(range.start, range.end)
5801                .expect("the queued container belongs to this tree");
5802            assert_eq!(container.byte_range(), range);
5803            let ancestry = self.recovery_ancestry(root);
5804            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5805                visitor.run_container_work(container, scope, &ancestry)
5806            });
5807            // A recovered walk may enqueue another partition inside this
5808            // region. Delay the outer claim until all nested regions have
5809            // drained, or their nodes would be skipped as already consumed.
5810            consumed.push((range.start, range.end));
5811        }
5812        self.consumed_fragment_regions.extend(consumed);
5813    }
5814
5815    /// Whether a work node lies entirely inside a byte region consumed by a
5816    /// fragmented export-class recovery (#938); such nodes were already indexed
5817    /// as members of the recovered class by the region reparse.
5818    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5819        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5820    }
5821
5822    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5823    /// candidate a recovery is about to reparse but has not yet turned into a
5824    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5825    /// prior structured recovery.
5826    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5827        self.consumed_fragment_regions
5828            .iter()
5829            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5830    }
5831
5832    /// Drive the container work loop from an explicit seed scope to completion. The
5833    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5834    /// stays alive for the whole traversal.
5835    ///
5836    /// Every ancestor question this walk asks is answered from `ancestry`, which
5837    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5838    /// node's position in the tree, which made a generated header with thousands
5839    /// of top-level declarations quadratic (#2361). The index is the caller's
5840    /// because it outlives any one walk: the file's tree is walked twice when a
5841    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5842    /// builds its own index for its own tree.
5843    fn run_container_work<'tree>(
5844        &mut self,
5845        node: Node<'tree>,
5846        scope: ScopeInfo,
5847        ancestry: &ParentIndex<'tree>,
5848    ) {
5849        self.drain_cpp_work(
5850            vec![CppWork::Container(CppContainer { node, scope })],
5851            ancestry,
5852        );
5853    }
5854
5855    /// The work loop itself, from whatever seed the caller built.
5856    ///
5857    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5858    /// that must walk only part of a reparsed tree seeds it with the sibling
5859    /// range it may walk instead, which keeps the `using namespace X;` scope
5860    /// accumulation `advance_cpp_siblings` performs.
5861    fn drain_cpp_work<'tree>(
5862        &mut self,
5863        mut stack: Vec<CppWork<'tree>>,
5864        ancestry: &ParentIndex<'tree>,
5865    ) {
5866        while let Some(work) = stack.pop() {
5867            match work {
5868                CppWork::Container(container) => {
5869                    push_cpp_container_work(container.node, container.scope, &mut stack);
5870                }
5871                CppWork::Siblings(siblings) => {
5872                    advance_cpp_siblings(siblings, self.source, &mut stack);
5873                }
5874                CppWork::Node(work) => {
5875                    if self.node_is_inside_consumed_fragment(work.node) {
5876                        continue;
5877                    }
5878                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5879                }
5880            }
5881        }
5882    }
5883
5884    /// Drain nodes selected by a bounded recovery walk without filtering them
5885    /// against consumed regions from the primary tree.
5886    fn drain_cpp_recovery_work<'tree>(
5887        &mut self,
5888        mut stack: Vec<CppWork<'tree>>,
5889        ancestry: &ParentIndex<'tree>,
5890    ) {
5891        while let Some(work) = stack.pop() {
5892            match work {
5893                CppWork::Container(container) => {
5894                    push_cpp_container_work(container.node, container.scope, &mut stack);
5895                }
5896                CppWork::Siblings(siblings) => {
5897                    advance_cpp_siblings(siblings, self.source, &mut stack);
5898                }
5899                CppWork::Node(work) => {
5900                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5901                }
5902            }
5903        }
5904    }
5905
5906    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5907    /// it only when the entire region is member-shaped. This validation must happen
5908    /// before registering the recovered class because a rejected speculative range
5909    /// must not leak into the ordinary recovery path.
5910    fn reparse_fragmented_export_class_members(
5911        &self,
5912        fragmented: &FragmentedExportBody,
5913        class_name: &str,
5914    ) -> Option<FragmentedExportMembers> {
5915        if fragmented.reparse_start >= fragmented.reparse_end {
5916            return None;
5917        }
5918        let tree = cpp_reparse_fragmented_class_body(
5919            self.source,
5920            fragmented.reparse_start,
5921            fragmented.reparse_end,
5922        )?;
5923        if cpp_reparsed_members_are_indexable_for_class(
5924            tree.root_node(),
5925            self.source,
5926            Some(class_name),
5927        ) {
5928            return Some(FragmentedExportMembers::Complete(tree));
5929        }
5930        let has_conditional_constructor = {
5931            let root = tree.root_node();
5932            let mut cursor = root.walk();
5933            root.named_children(&mut cursor).any(|child| {
5934                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5935            })
5936        };
5937        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5938    }
5939
5940    /// Index an already validated fragmented body as members of `class_unit`. The
5941    /// region reparse keeps each member's exact original byte and line positions.
5942    fn visit_fragmented_export_class_members(
5943        &mut self,
5944        origin: Node<'_>,
5945        outcome: FragmentedExportMembers,
5946        class_unit: CodeUnit,
5947        scope: &ScopeInfo,
5948    ) -> bool {
5949        self.with_recovery_source_context(origin, |visitor| {
5950            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5951        })
5952    }
5953
5954    fn visit_fragmented_export_class_members_in_context(
5955        &mut self,
5956        outcome: FragmentedExportMembers,
5957        class_unit: CodeUnit,
5958        scope: &ScopeInfo,
5959    ) -> bool {
5960        let (tree, complete) = match outcome {
5961            FragmentedExportMembers::Complete(tree) => (tree, true),
5962            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5963        };
5964        let root = tree.root_node();
5965        let class_name = class_unit.identifier().to_string();
5966        let member_scope = ScopeInfo {
5967            // A recovered export-macro class may borrow its namespace from an
5968            // earlier forward declaration even when the malformed node itself
5969            // sits at file scope. Use the recovered class identity as the
5970            // authoritative package for reparsed members as well.
5971            package_name: class_unit.package_name().to_string(),
5972            module: scope.module.clone(),
5973            class_unit: Some(class_unit),
5974            template_signature: scope.template_signature.clone(),
5975            template_metadata: None,
5976            declarations_are_fields: true,
5977            recovered_specialization_member_scope: false,
5978            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5979            visible_using_declarations: scope.visible_using_declarations.clone(),
5980        };
5981        let reparsed_ancestry = self.recovery_ancestry(root);
5982        if !complete {
5983            // A conditional beginning immediately after an access label can
5984            // fragment one constructor declaration while leaving the rest of
5985            // the class body as unsafe statement soup. Recover only that
5986            // structurally proven constructor and leave the outer-tree
5987            // siblings unconsumed for their ordinary walk.
5988            let mut cursor = root.walk();
5989            let constructors = root
5990                .named_children(&mut cursor)
5991                .filter_map(|child| {
5992                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5993                })
5994                .collect::<Vec<_>>();
5995            // The reparsed region is its own tree, so this drain walks it with
5996            // its own parent index.
5997            for constructor in constructors {
5998                let mut stack = Vec::new();
5999                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6000                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6001            }
6002            return false;
6003        }
6004        let mut cursor = root.walk();
6005        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6006        for member in members.iter().copied().filter(|member| {
6007            cpp_reparsed_defaulted_constructor_is_indexable(
6008                *member,
6009                &members,
6010                Some(class_name.as_str()),
6011                self.source,
6012            )
6013        }) {
6014            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6015        }
6016        for member in members.iter().copied().filter(|member| {
6017            cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6018        }) {
6019            self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6020        }
6021        // The reparsed region is its own tree, so this walk indexes it itself.
6022        self.run_container_work(root, member_scope, &reparsed_ancestry);
6023        true
6024    }
6025
6026    fn visit_recovered_fragment_constructor<'tree>(
6027        &mut self,
6028        range: std::ops::Range<usize>,
6029        constructor_body: Node<'tree>,
6030        class_declaration: Node<'tree>,
6031        class_unit: &CodeUnit,
6032        scope: &ScopeInfo,
6033        ancestry: &ParentIndex<'tree>,
6034    ) {
6035        self.with_recovery_source_context(class_declaration, |visitor| {
6036            visitor.visit_recovered_fragment_constructor_in_context(
6037                range,
6038                constructor_body,
6039                class_declaration,
6040                class_unit,
6041                scope,
6042                ancestry,
6043            );
6044        });
6045    }
6046
6047    fn visit_recovered_fragment_constructor_in_context<'tree>(
6048        &mut self,
6049        range: std::ops::Range<usize>,
6050        constructor_body: Node<'tree>,
6051        class_declaration: Node<'tree>,
6052        class_unit: &CodeUnit,
6053        scope: &ScopeInfo,
6054        ancestry: &ParentIndex<'tree>,
6055    ) {
6056        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6057            return;
6058        };
6059        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6060            tree.root_node(),
6061            range.start,
6062            class_unit.identifier(),
6063            self.source,
6064        ) else {
6065            return;
6066        };
6067        let member_scope = ScopeInfo {
6068            package_name: class_unit.package_name().to_string(),
6069            module: scope.module.clone(),
6070            class_unit: Some(class_unit.clone()),
6071            template_signature: scope.template_signature.clone(),
6072            template_metadata: None,
6073            declarations_are_fields: true,
6074            recovered_specialization_member_scope: false,
6075            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6076            visible_using_declarations: scope.visible_using_declarations.clone(),
6077        };
6078        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6079        else {
6080            return;
6081        };
6082        debug_assert_eq!(function.name, class_unit.identifier());
6083        let code_unit = function.code_unit(self.file.clone());
6084        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6085        self.add_declaration_with_range(
6086            &recovered_ancestry,
6087            code_unit.clone(),
6088            Range {
6089                start_byte: function_declarator.start_byte(),
6090                end_byte: constructor_body.end_byte(),
6091                start_line: function_declarator.start_position().row + 1,
6092                end_line: constructor_body.end_position().row + 1,
6093            },
6094            Some(function_declarator),
6095            None,
6096            None,
6097        );
6098        let declaration = *self.source_links[&code_unit]
6099            .last()
6100            .expect("recovered constructor source");
6101        {
6102            let mut properties = self.source_properties.borrow_mut();
6103            let fact = properties
6104                .get_mut(&declaration)
6105                .expect("recovered constructor source fact");
6106            fact.occurrence_role =
6107                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6108            fact.callable_is_constructor = true;
6109        }
6110        self.add_signature_with_metadata(
6111            code_unit.clone(),
6112            cpp_signature_metadata(
6113                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6114                function_declarator,
6115                cpp_callable_linkage(class_declaration, self.source, ancestry),
6116                self.source,
6117                &recovered_ancestry,
6118            )
6119            .with_declaration_only(false),
6120        );
6121        self.parsed.add_child(class_unit.clone(), code_unit);
6122    }
6123
6124    fn visit_recovered_fragment_prefix_members<'tree>(
6125        &mut self,
6126        origin: Node<'_>,
6127        root: Node<'tree>,
6128        constructor_start: usize,
6129        class_unit: &CodeUnit,
6130        scope: &ScopeInfo,
6131        ancestry: &ParentIndex<'tree>,
6132    ) {
6133        self.with_recovery_source_context(origin, |visitor| {
6134            visitor.visit_recovered_fragment_prefix_members_in_context(
6135                root,
6136                constructor_start,
6137                class_unit,
6138                scope,
6139                ancestry,
6140            );
6141        });
6142    }
6143
6144    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6145        &mut self,
6146        root: Node<'tree>,
6147        constructor_start: usize,
6148        class_unit: &CodeUnit,
6149        scope: &ScopeInfo,
6150        ancestry: &ParentIndex<'tree>,
6151    ) {
6152        let member_scope = ScopeInfo {
6153            package_name: class_unit.package_name().to_string(),
6154            module: scope.module.clone(),
6155            class_unit: Some(class_unit.clone()),
6156            template_signature: scope.template_signature.clone(),
6157            template_metadata: None,
6158            declarations_are_fields: true,
6159            recovered_specialization_member_scope: false,
6160            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6161            visible_using_declarations: scope.visible_using_declarations.clone(),
6162        };
6163        let mut root_cursor = root.walk();
6164        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6165        let mut stack = vec![root];
6166        while let Some(current) = stack.pop() {
6167            if current.kind() == "comment" || current.start_byte() >= constructor_start {
6168                continue;
6169            }
6170            if current
6171                .parent()
6172                .is_some_and(|parent| same_node(parent, root))
6173                && cpp_reparsed_defaulted_constructor_is_indexable(
6174                    current,
6175                    &members,
6176                    Some(class_unit.identifier()),
6177                    self.source,
6178                )
6179            {
6180                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6181                continue;
6182            }
6183            if current.end_byte() <= constructor_start
6184                && current.kind() != "translation_unit"
6185                && current.kind() != "labeled_statement"
6186                && current.kind() != "ERROR"
6187            {
6188                let mut work_stack = Vec::new();
6189                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6190                self.drain_cpp_recovery_work(work_stack, ancestry);
6191                continue;
6192            }
6193            if matches!(
6194                current.kind(),
6195                "translation_unit" | "labeled_statement" | "ERROR"
6196            ) {
6197                let mut cursor = current.walk();
6198                stack.extend(current.named_children(&mut cursor));
6199            }
6200        }
6201    }
6202
6203    fn visit_recovered_defaulted_constructor(
6204        &mut self,
6205        statement: Node<'_>,
6206        scope: &ScopeInfo,
6207        ancestry: &ParentIndex<'_>,
6208    ) {
6209        let Some(parent) = scope.class_unit.clone() else {
6210            return;
6211        };
6212        let Some(assignment) = statement.named_child(0) else {
6213            return;
6214        };
6215        let Some(call) = assignment.child_by_field_name("left") else {
6216            return;
6217        };
6218        let Some(function_name) = call.child_by_field_name("function") else {
6219            return;
6220        };
6221        let Some(arguments) = call.child_by_field_name("arguments") else {
6222            return;
6223        };
6224        if function_name.kind() != "identifier"
6225            || node_text(function_name, self.source) != parent.identifier()
6226            || arguments.kind() != "argument_list"
6227            || arguments.named_child_count() != 0
6228        {
6229            return;
6230        }
6231        let function = FunctionInfo {
6232            package_name: scope.package_name.clone(),
6233            owner: Some(CppMemberOwner::Unit(parent.clone())),
6234            name: parent.identifier().to_string(),
6235            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6236        };
6237        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6238        if self.parsed.contains_declaration(&code_unit) {
6239            return;
6240        }
6241        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6242        let metadata = SignatureMetadata::new(
6243            normalize_cpp_whitespace(node_text(statement, self.source)),
6244            Vec::new(),
6245        )
6246        .with_declaration_only(true)
6247        .with_callable_arity(CallableArity::exact(0))
6248        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6249        // The reparse proves class ownership, but carries no original class
6250        // access ladder. Preserve unknown access rather than guessing it.
6251        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6252        self.add_signature_with_metadata(code_unit.clone(), metadata);
6253        if let Some(declaration) = self
6254            .source_links
6255            .get(&code_unit)
6256            .and_then(|links| links.last())
6257            .copied()
6258        {
6259            let mut properties = self.source_properties.borrow_mut();
6260            let fact = properties
6261                .get_mut(&declaration)
6262                .expect("recovered defaulted constructor source fact");
6263            fact.occurrence_role =
6264                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6265            fact.callable_is_constructor = true;
6266        }
6267        self.parsed.add_child(parent, code_unit);
6268    }
6269
6270    /// Recover a constructor declaration a statement-context class-body
6271    /// reparse read as a call or parenthesized variable declarator. The
6272    /// statement is validated and its parameters read from a synthetic class
6273    /// context, where the constructor declaration parses as a real function
6274    /// declarator; the declaration itself is minted from the original
6275    /// statement node, so every range keeps its original byte positions.
6276    fn visit_bare_constructor_expression<'tree>(
6277        &mut self,
6278        statement: Node<'tree>,
6279        scope: &ScopeInfo,
6280        ancestry: &ParentIndex<'tree>,
6281    ) -> bool {
6282        let Some(parent) = scope.class_unit.clone() else {
6283            return false;
6284        };
6285        const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6286        let region = &self.source[statement.start_byte()..statement.end_byte()];
6287        let parsed = crate::graph::syntax::parse_sentinel(
6288            PREFIX,
6289            crate::graph::syntax::SentinelInput::region(region),
6290            " };",
6291        );
6292        let (synthetic, tree) = (parsed.source, parsed.tree);
6293        let root = tree.root_node();
6294        if root.has_error() {
6295            return false;
6296        }
6297        let mut declarator = None;
6298        let mut stack = vec![root];
6299        while let Some(current) = stack.pop() {
6300            if current.kind() == "function_declarator" {
6301                if declarator.is_some() {
6302                    return false;
6303                }
6304                declarator = Some(current);
6305                continue;
6306            }
6307            let mut cursor = current.walk();
6308            stack.extend(current.named_children(&mut cursor));
6309        }
6310        let Some(declarator) = declarator else {
6311            return false;
6312        };
6313        let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6314            return false;
6315        };
6316        let signature = cpp_parameter_signature(parameters_node, &synthetic);
6317        let mut parameter_labels = Vec::new();
6318        let mut parameter_cursor = parameters_node.walk();
6319        for parameter in parameters_node.named_children(&mut parameter_cursor) {
6320            if matches!(
6321                parameter.kind(),
6322                "parameter_declaration" | "optional_parameter_declaration"
6323            ) && let Some(name) = parameter
6324                .child_by_field_name("declarator")
6325                .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6326            {
6327                parameter_labels.push(name);
6328            }
6329        }
6330        let arity = parameter_labels.len();
6331        let function = FunctionInfo {
6332            package_name: scope.package_name.clone(),
6333            owner: Some(CppMemberOwner::Unit(parent.clone())),
6334            name: parent.identifier().to_string(),
6335            signature,
6336        };
6337        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6338        if self.parsed.contains_declaration(&code_unit) {
6339            return true;
6340        }
6341        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6342        let metadata = SignatureMetadata::with_parameter_labels(
6343            normalize_cpp_whitespace(node_text(statement, self.source)),
6344            parameter_labels,
6345        )
6346        .with_declaration_only(true)
6347        .with_callable_arity(CallableArity::exact(arity))
6348        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6349        // The reparse proves class ownership, but carries no original
6350        // class access ladder. Preserve unknown access rather than
6351        // guessing it.
6352        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6353        self.add_signature_with_metadata(code_unit.clone(), metadata);
6354        if let Some(declaration) = self
6355            .source_links
6356            .get(&code_unit)
6357            .and_then(|links| links.last())
6358            .copied()
6359        {
6360            let mut properties = self.source_properties.borrow_mut();
6361            let fact = properties
6362                .get_mut(&declaration)
6363                .expect("recovered constructor source fact");
6364            fact.occurrence_role =
6365                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6366            fact.callable_is_constructor = true;
6367        }
6368        self.parsed.add_child(parent, code_unit);
6369        true
6370    }
6371
6372    /// Capture ancestors only when a proven recovery enters another tree. The
6373    /// ordinary declaration driver never performs this additional ancestor walk.
6374    fn with_recovery_source_context<T>(
6375        &mut self,
6376        node: Node<'_>,
6377        walk: impl FnOnce(&mut Self) -> T,
6378    ) -> T {
6379        self.with_recovery_source_context_at(node, node, walk)
6380    }
6381
6382    /// [`Self::with_recovery_source_context`] for a recovery whose content
6383    /// begins inside `node` at `preprocessor_anchor`: the lexical owners come
6384    /// from `node`, the preprocessor facts from the anchor. The two differ when
6385    /// the malformed node swallowed a branch directive that precedes the
6386    /// recovered content.
6387    fn with_recovery_source_context_at<T>(
6388        &mut self,
6389        node: Node<'_>,
6390        preprocessor_anchor: Node<'_>,
6391        walk: impl FnOnce(&mut Self) -> T,
6392    ) -> T {
6393        use crate::graph::resolver::{
6394            enclosing_namespace_context, merge_preprocessor_guards,
6395            preprocessor_conditional_family_for_declaration_with_cache,
6396            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6397        };
6398        let previous = self.active_source_context.take();
6399        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6400        let inherited = (!primary).then_some(previous.as_ref()).flatten();
6401        let local_guards = preprocessor_guard_environment_with_cache(
6402            preprocessor_anchor,
6403            self.source,
6404            &mut self.preprocessor_context_cache,
6405        );
6406        let guards = match inherited {
6407            Some(outer) => outer
6408                .guards
6409                .as_ref()
6410                .zip(local_guards.as_ref())
6411                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6412            None => local_guards,
6413        };
6414        let local_namespace = enclosing_namespace_context(node, self.source);
6415        let namespace = match (
6416            inherited.and_then(|outer| outer.child_namespace.as_ref()),
6417            local_namespace,
6418        ) {
6419            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6420            (Some(outer), None) => Some(outer.clone()),
6421            (None, local) => local,
6422        };
6423        let outer_lexical_scope =
6424            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6425        let mut local_owners = Vec::new();
6426        let mut parent = node.parent();
6427        while let Some(ancestor) = parent {
6428            if matches!(
6429                ancestor.kind(),
6430                "namespace_definition"
6431                    | "class_specifier"
6432                    | "struct_specifier"
6433                    | "union_specifier"
6434                    | "enum_specifier"
6435            ) && let Some(name) = ancestor.child_by_field_name("name")
6436            {
6437                local_owners.push(node_text(name, self.source).to_owned());
6438            }
6439            parent = ancestor.parent();
6440        }
6441        local_owners.reverse();
6442        let mut child_lexical_scope = outer_lexical_scope.clone();
6443        child_lexical_scope.extend(local_owners);
6444        if matches!(
6445            node.kind(),
6446            "namespace_definition"
6447                | "class_specifier"
6448                | "struct_specifier"
6449                | "union_specifier"
6450                | "enum_specifier"
6451        ) && let Some(name) = node.child_by_field_name("name")
6452        {
6453            child_lexical_scope.push(node_text(name, self.source).to_owned());
6454        }
6455        let child_namespace = if node.kind() == "namespace_definition" {
6456            node.child_by_field_name("name")
6457                .map(|name| {
6458                    let local = node_text(name, self.source);
6459                    namespace
6460                        .as_ref()
6461                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6462                })
6463                .or_else(|| namespace.clone())
6464        } else {
6465            namespace.clone()
6466        };
6467        let intern = |family: Node<'_>| {
6468            let mut collector = self.source_collector.borrow_mut();
6469            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6470                collector.intern_node(family)
6471            } else {
6472                collector
6473                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6474            }
6475        };
6476        let family = preprocessor_conditional_family_node(preprocessor_anchor)
6477            .map(intern)
6478            .or_else(|| inherited.and_then(|outer| outer.family));
6479        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6480            preprocessor_anchor,
6481            &mut self.preprocessor_context_cache,
6482        )
6483        .map(intern)
6484        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6485        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6486            preprocessor_anchor,
6487            self.source,
6488            &mut self.preprocessor_context_cache,
6489        );
6490        let callable_guard_completion_byte = match inherited {
6491            Some(outer) => local_completion
6492                .zip(outer.callable_guard_completion_byte)
6493                .map(|(local, outer)| local.max(outer)),
6494            None => local_completion,
6495        };
6496        self.active_source_context = Some(CppActiveSourceContext {
6497            guards,
6498            namespace: child_namespace.clone(),
6499            child_namespace,
6500            outer_lexical_scope: child_lexical_scope.clone(),
6501            child_lexical_scope,
6502            family,
6503            exhaustive_family,
6504            callable_guard_completion_byte,
6505        });
6506        let result = walk(self);
6507        self.active_source_context = previous;
6508        result
6509    }
6510
6511    fn visit_node<'tree>(
6512        &mut self,
6513        node: Node<'tree>,
6514        scope: &ScopeInfo,
6515        stack: &mut Vec<CppWork<'tree>>,
6516        ancestry: &ParentIndex<'tree>,
6517    ) {
6518        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6519            self.visit_node(node, &recovered_scope, stack, ancestry);
6520            return;
6521        }
6522        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6523            self.visit_node(node, &recovered_scope, stack, ancestry);
6524            return;
6525        }
6526        if scope.class_unit.is_some()
6527            && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6528        {
6529            self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6530            return;
6531        }
6532        // Fragmented-class recovery below may consume a malformed function
6533        // envelope before the ordinary kind dispatch runs. Recover any
6534        // export-macro class embedded in that envelope first; the strict class
6535        // head/base/body predicate is independent of which later recovery owns
6536        // the surrounding parser fragment.
6537        if node.kind() == "function_definition" && node.has_error() {
6538            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6539        }
6540        if let Some(FragmentedClassRecovery {
6541            declaration_node: class_node,
6542            name,
6543            raw_supertypes,
6544            body: fragmented,
6545        }) = fragmented_class_body(node, self.source)
6546        {
6547            let displaced_namespace_items =
6548                displaced_fragment_namespace_geometry(node, self.source)
6549                    .map(|boundary| boundary.namespace_items)
6550                    .unwrap_or_default();
6551            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6552            let mut class_stack = Vec::new();
6553            // When the full body cannot be safely reparsed, the original class
6554            // node still proves ownership for its parser-visible prefix.
6555            let parser_visible_body =
6556                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6557                    .then(|| cpp_body_node(class_node))
6558                    .flatten();
6559            let class_unit = self.visit_named_class_like_shape(
6560                class_node,
6561                name,
6562                parser_visible_body,
6563                true,
6564                Some(fragmented.class_range),
6565                Some(raw_supertypes),
6566                Some(extract_cpp_base_facts(class_node, self.source)),
6567                scope,
6568                &mut class_stack,
6569                ancestry,
6570            );
6571            let member_scope = ScopeInfo {
6572                package_name: class_unit.package_name().to_string(),
6573                module: scope.module.clone(),
6574                class_unit: Some(class_unit.clone()),
6575                template_signature: scope.template_signature.clone(),
6576                template_metadata: None,
6577                declarations_are_fields: true,
6578                recovered_specialization_member_scope: false,
6579                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6580                visible_using_declarations: scope.visible_using_declarations.clone(),
6581            };
6582            let complete = outcome.is_some_and(|outcome| {
6583                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6584            });
6585            if complete {
6586                self.consumed_fragment_regions
6587                    .push((node.start_byte(), fragmented.class_range.end_byte));
6588            } else {
6589                // A macro-constrained member can make the full body reparse
6590                // unsafe while tree-sitter still exposes later class members
6591                // as bounded siblings up to the displaced `}`/`;`. Keep the
6592                // structurally proven class/base declaration and re-own those
6593                // sibling nodes under it. They retain their original parser
6594                // nodes and exact ranges; the close boundary comes solely from
6595                // `fragmented_class_body`.
6596                // Template wrappers put the escaped members beside the
6597                // template rather than beside its malformed declaration.
6598                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6599                    if candidate.start_byte() >= fragmented.reparse_end {
6600                        break;
6601                    }
6602                    if cpp_fragment_sibling_is_class_member(
6603                        candidate,
6604                        fragmented.reparse_end,
6605                        self.source,
6606                    ) {
6607                        self.recovered_class_sibling_scopes
6608                            .insert(candidate.id(), member_scope.clone());
6609                    }
6610                }
6611                // The macro-declaration grammar keeps the class head and its
6612                // parser-visible prefix members inside the fragmented
6613                // container itself, where there is no separate body node for
6614                // `visit_named_class_like_shape` to seed. Walk the container
6615                // with the member scope so those prefix members are owned by
6616                // the class; the head's bare tokens mint nothing on their own.
6617                if class_node.kind() == "ERROR" && class_node.has_error() {
6618                    stack.push(CppWork::Container(CppContainer {
6619                        node: class_node,
6620                        scope: member_scope,
6621                    }));
6622                }
6623            }
6624            for item in displaced_namespace_items {
6625                self.recovered_class_sibling_scopes
6626                    .insert(item.id(), scope.clone());
6627            }
6628            stack.extend(class_stack);
6629            return;
6630        }
6631        if self.visit_folded_aggregate(node, scope) {
6632            return;
6633        }
6634        match node.kind() {
6635            "template_declaration" => {
6636                if let Some(recovered) =
6637                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6638                {
6639                    let mut template_scope = scope.clone();
6640                    template_scope.template_signature =
6641                        cpp_template_signature(node, recovered.declaration_node, self.source);
6642                    template_scope.template_metadata =
6643                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6644                    let raw_supertypes =
6645                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6646                    let mut class_stack = Vec::new();
6647                    let class_unit = self.visit_named_class_like_shape(
6648                        recovered.class_node,
6649                        recovered.name,
6650                        Some(recovered.body),
6651                        true,
6652                        Some(recovered.range),
6653                        raw_supertypes,
6654                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6655                        &template_scope,
6656                        &mut class_stack,
6657                        ancestry,
6658                    );
6659                    self.parsed.record_materialization(
6660                        MaterializationRecord::RecoveredDeclaration {
6661                            recovery: recovered.range,
6662                            unit: class_unit.clone(),
6663                        },
6664                    );
6665                    let member_scope = ScopeInfo {
6666                        package_name: template_scope.package_name.clone(),
6667                        module: template_scope.module.clone(),
6668                        class_unit: Some(class_unit.clone()),
6669                        template_signature: template_scope.template_signature.clone(),
6670                        template_metadata: None,
6671                        declarations_are_fields: true,
6672                        recovered_specialization_member_scope: recovered
6673                            .class_node
6674                            .child_by_field_name("name")
6675                            .is_some_and(|name| name.kind() == "template_type"),
6676                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6677                        visible_using_declarations: template_scope
6678                            .visible_using_declarations
6679                            .clone(),
6680                    };
6681                    for tail_member in recovered.tail_members.into_iter().rev() {
6682                        stack.push(CppWork::Node(CppNodeWork {
6683                            node: tail_member,
6684                            scope: member_scope.clone(),
6685                        }));
6686                    }
6687                    stack.extend(class_stack);
6688                    for sibling in recovered.member_siblings {
6689                        self.recovered_class_sibling_scopes
6690                            .insert(sibling.id(), member_scope.clone());
6691                    }
6692                    return;
6693                }
6694                for index in (0..node.named_child_count()).rev() {
6695                    let Some(child) = node.named_child(index) else {
6696                        continue;
6697                    };
6698                    if matches!(
6699                        child.kind(),
6700                        "class_specifier"
6701                            | "struct_specifier"
6702                            | "union_specifier"
6703                            | "enum_specifier"
6704                            | "function_definition"
6705                            | "declaration"
6706                            | "field_declaration"
6707                            | "alias_declaration"
6708                            | "namespace_definition"
6709                    ) {
6710                        let mut template_scope = scope.clone();
6711                        template_scope.template_signature =
6712                            cpp_template_signature(node, child, self.source);
6713                        template_scope.template_metadata =
6714                            cpp_template_metadata(node, child, self.source, ancestry);
6715                        if let Some(recovered) = recover_fragmented_partial_specialization(
6716                            node,
6717                            child,
6718                            self.source,
6719                            ancestry,
6720                        ) {
6721                            let code_unit = self.visit_named_class_like_shape(
6722                                recovered.declaration_node,
6723                                recovered.name,
6724                                None,
6725                                true,
6726                                Some(recovered.range),
6727                                None,
6728                                None,
6729                                &template_scope,
6730                                stack,
6731                                ancestry,
6732                            );
6733                            self.parsed.record_materialization(
6734                                MaterializationRecord::RecoveredDeclaration {
6735                                    recovery: recovered.range,
6736                                    unit: code_unit.clone(),
6737                                },
6738                            );
6739                            let mut member_scope = template_scope.clone();
6740                            member_scope.class_unit = Some(code_unit);
6741                            member_scope.declarations_are_fields = true;
6742                            member_scope.recovered_specialization_member_scope = true;
6743                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6744                                stack.push(CppWork::Node(CppNodeWork {
6745                                    node: prefix_member,
6746                                    scope: member_scope.clone(),
6747                                }));
6748                            }
6749                            for sibling in recovered.member_siblings {
6750                                self.recovered_class_sibling_scopes
6751                                    .insert(sibling.id(), member_scope.clone());
6752                            }
6753                            for following in recovered.following_declarations.into_iter().rev() {
6754                                stack.push(CppWork::Node(CppNodeWork {
6755                                    node: following,
6756                                    scope: scope.clone(),
6757                                }));
6758                            }
6759                            return;
6760                        }
6761                        stack.push(CppWork::Node(CppNodeWork {
6762                            node: child,
6763                            scope: template_scope,
6764                        }));
6765                    }
6766                }
6767            }
6768            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6769            "linkage_specification" => {
6770                if let Some(body) = cpp_body_node(node) {
6771                    // A linkage specification either wraps a direct declaration or a
6772                    // braced declaration_list. Visit the direct item itself so
6773                    // declaration recoveries still run on its original syntax node.
6774                    let work = if body.kind() == "declaration_list" {
6775                        CppWork::Container(CppContainer {
6776                            node: body,
6777                            scope: scope.clone(),
6778                        })
6779                    } else {
6780                        CppWork::Node(CppNodeWork {
6781                            node: body,
6782                            scope: scope.clone(),
6783                        })
6784                    };
6785                    stack.push(work);
6786                } else {
6787                    stack.push(CppWork::Container(CppContainer {
6788                        node,
6789                        scope: scope.clone(),
6790                    }));
6791                }
6792            }
6793            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6794                self.visit_class_like(node, scope, stack, ancestry)
6795            }
6796            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6797            // A bare namespace-begin sentinel can make tree-sitter promote the
6798            // wrapped declaration to an ERROR node instead of the usual bogus
6799            // function_definition envelope. Keep the recovery entry point on
6800            // the same structured path for both shapes; ordinary ERROR nodes
6801            // retain their declaration-preserving wrapper traversal when the
6802            // sentinel predicate does not match.
6803            "ERROR" => {
6804                // A standalone field-list invocation has no declaration wrapper.
6805                // Claim only an envelope consisting entirely of known invocations.
6806                if node.named_child_count() > 0
6807                    && node.children(&mut node.walk()).all(|child| {
6808                        child.kind() == "identifier"
6809                            && self
6810                                .object_macro_fields
6811                                .contains_key(node_text(child, self.source))
6812                    })
6813                    && self.visit_bare_object_macro_fields(node, scope)
6814                {
6815                    return;
6816                }
6817                self.visit_object_macro_error_classes(node, scope);
6818                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6819                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6820                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6821                        return;
6822                    }
6823                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6824                        return;
6825                    }
6826                    self.visit_macro_swallowed_function_declarations(node, scope);
6827                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6828                    self.visit_stranded_class_members(node, scope, ancestry);
6829                    stack.push(CppWork::Container(CppContainer {
6830                        node,
6831                        scope: scope.clone(),
6832                    }));
6833                }
6834            }
6835            "declaration" => {
6836                if node.has_error() {
6837                    // Object-like macro lines before a function-like
6838                    // export-macro class head keep the head in a `declaration`
6839                    // rather than in an `ERROR` (#2924). It is the same head
6840                    // and the same following-sibling body, so it takes the same
6841                    // recovery; without it the head's macro invocation indexes
6842                    // as a field named after the macro.
6843                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6844                        return;
6845                    }
6846                }
6847                // The macro-declaration grammar reads a K&R prototype macro
6848                // line as a clean declaration with the name and the macro
6849                // displaced into `macro_modifier` slots, so the prototype
6850                // candidate gate runs on clean declarations too; the candidates
6851                // themselves remain the admission proof.
6852                self.visit_prototype_macro_declarations(node, scope, ancestry);
6853                if self.node_is_inside_consumed_fragment(node) {
6854                    // The whole declaration was a recovered K&R
6855                    // prototype macro invocation (issue #2932); the
6856                    // reparse above already indexed its Function.
6857                    // Falling through to the ordinary declaration
6858                    // visitor would additionally mint the swapped-field
6859                    // wreckage this recovery exists to replace.
6860                    return;
6861                }
6862                if scope.class_unit.is_some()
6863                    && scope.declarations_are_fields
6864                    && scope.recovered_specialization_member_scope
6865                    && let Some(alias_name) =
6866                        recovered_using_declaration_alias_name(node, self.source)
6867                {
6868                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6869                } else {
6870                    self.visit_declaration(
6871                        node,
6872                        scope,
6873                        scope.declarations_are_fields,
6874                        stack,
6875                        ancestry,
6876                    )
6877                }
6878            }
6879            // A K&R prototype macro invocation with a pointer return type and a
6880            // simple argument list parses with no ERROR node at all: tree-sitter
6881            // reads `T *name _(( args ));` as a multiplication of a type by a
6882            // call (`T * name_(...)`), wrapped in an `expression_statement`
6883            // that `has_error()` only because of a `MISSING "::"` inside it
6884            // (issue #2932). Nothing else mints a declaration from an
6885            // `expression_statement` at declaration scope today, so there is
6886            // no existing behavior to preserve here if this node is not the
6887            // K&R shape; the admission gate inside
6888            // `visit_prototype_macro_declarations` (exactly one clean function
6889            // prototype spanning the recovered run) is what keeps this arm
6890            // safe on an ordinary errorful expression statement that happens
6891            // to contain nested parentheses.
6892            "expression_statement" => {
6893                if node.has_error() {
6894                    self.visit_prototype_macro_declarations(node, scope, ancestry);
6895                }
6896            }
6897            "field_declaration" => {
6898                // A class-body K&R prototype macro line is the same
6899                // `macro_modifier` shape as a file-scope one, so it takes the
6900                // same candidate gate and reparse (#6902). Only this node's own
6901                // recovery ends its visit: a recovery walk can reach members of
6902                // a region another recovery consumed, and those stay walked.
6903                if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6904                    self.visit_declaration(node, scope, true, stack, ancestry)
6905                }
6906            }
6907            "preproc_call" => self.visit_preproc_call(node),
6908            "type_definition" | "alias_declaration" => {
6909                self.visit_type_declaration(node, scope, stack, ancestry)
6910            }
6911            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6912            // `#include` is collected by the coordinated primary events before the
6913            // walk, so a directive the container walk never reaches -- inside
6914            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6915            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6916            // include claim.
6917            "preproc_include" => {}
6918            // The grammar represents a split extern-C condition as sibling
6919            // markers; declarations between them remain unconditional file
6920            // scope items and need no special declaration metadata.
6921            "preproc_linkage_open" | "preproc_linkage_close" => {}
6922            kind if preserves_declaration_scope_through_wrapper(
6923                kind,
6924                scope.class_unit.is_some(),
6925            ) =>
6926            {
6927                // A preprocessor conditional gates every declaration inside it
6928                // on a configuration this analyzer never evaluates; record the
6929                // interval so declaration state can say so (issue #1476). The
6930                // else/elif branches are children of the `preproc_if` node, so
6931                // recording the openers covers every branch.
6932                if kind == "labeled_statement" {
6933                    self.visit_access_label_constructor(node, scope, ancestry);
6934                }
6935                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6936                    let mut range = cpp_declaration_range(node);
6937                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6938                        range.end_byte = boundary.end_byte;
6939                        range.end_line = boundary.end_line;
6940                    }
6941                    self.parsed.record_materialization(
6942                        MaterializationRecord::ConfigurationConditional { range },
6943                    );
6944                    if node.has_error() {
6945                        // A malformed export-macro class can close the namespace
6946                        // node early while the enclosing include guard still owns
6947                        // the remaining class-head/body pairs. The ordinary walk
6948                        // cannot carry the lost namespace through those promoted
6949                        // siblings. Scan only structured ERROR nodes in this
6950                        // already-malformed conditional; the pair recovery's
6951                        // exact class/macro/body predicate remains the admission
6952                        // gate, and its namespace lifting restores the owner.
6953                        let mut candidates = vec![node];
6954                        while let Some(candidate) = candidates.pop() {
6955                            // An `ERROR` still inside a namespace body is one
6956                            // the ordinary walk reaches with that namespace in
6957                            // scope. Claiming it here registers the recovered
6958                            // class at file scope and the consumed region then
6959                            // suppresses the walk that would have named it
6960                            // correctly -- which is why Botan's `DL_Group` was
6961                            // `DL_Group` and not `Botan.DL_Group` in the real
6962                            // header, where an include guard wraps the
6963                            // namespace, and was right in a fixture without one
6964                            // (#2552).
6965                            if candidate.kind() == "ERROR"
6966                                && !cpp_is_inside_namespace_body(candidate, ancestry)
6967                                && self.visit_function_like_export_class_pair(
6968                                    candidate, scope, stack, ancestry,
6969                                )
6970                            {
6971                                continue;
6972                            }
6973                            for index in (0..candidate.named_child_count()).rev() {
6974                                candidates.push(
6975                                    candidate
6976                                        .named_child(index)
6977                                        .expect("index below the node's own named child count"),
6978                                );
6979                            }
6980                        }
6981                    }
6982                }
6983                stack.push(CppWork::Container(CppContainer {
6984                    node,
6985                    scope: scope.clone(),
6986                }))
6987            }
6988            // A macro invocation the parser could not group leaves its head in
6989            // whatever slot was free. The `function_definition` and `ERROR`
6990            // arms above try the same recovery on the envelopes they get; a
6991            // flattened run leaves a bare `identifier`/`type_identifier`, and a
6992            // swallowed tail can land in a `parameter_declaration` (#3094).
6993            _ => {
6994                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6995            }
6996        }
6997    }
6998
6999    fn visit_macro_swallowed_function_declarations<'tree>(
7000        &mut self,
7001        envelope: Node<'tree>,
7002        scope: &ScopeInfo,
7003    ) {
7004        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7005            || envelope.kind() == "ERROR"
7006                && envelope
7007                    .parent()
7008                    .is_some_and(|parent| parent.kind() == "ERROR")
7009        {
7010            return;
7011        }
7012        let mut stack = (0..envelope.named_child_count())
7013            .filter_map(|index| envelope.named_child(index))
7014            .collect::<Vec<_>>();
7015        while let Some(node) = stack.pop() {
7016            if node.kind() == "function_declarator" {
7017                self.visit_error_swallowed_function_declaration(node, scope);
7018            }
7019            for child in named_children_iter(node) {
7020                stack.push(child);
7021            }
7022        }
7023    }
7024
7025    /// Index the declarations an attribute-like macro invocation swallowed into
7026    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
7027    /// the shape and why the parser produces it.
7028    fn visit_macro_wrapped_declarations<'tree>(
7029        &mut self,
7030        envelope: Node<'tree>,
7031        scope: &ScopeInfo,
7032        ancestry: &ParentIndex<'tree>,
7033    ) {
7034        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7035        if recovered.is_empty() {
7036            return;
7037        }
7038        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7039        self.record_recovered_declarations(recovery, envelope, |visitor| {
7040            for declaration in recovered {
7041                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7042            }
7043        });
7044    }
7045
7046    /// Index the declarations a macro invocation collapsed. See
7047    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
7048    /// produces it. Returns whether it claimed the region beginning at
7049    /// `envelope`.
7050    ///
7051    /// The parser's own nodes cannot be read the way the swallowed tail of the
7052    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
7053    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
7054    /// with no declarator left at all, and the declarators that do survive on
7055    /// the envelope's declarator spine pair one declaration's name with the
7056    /// *next* declaration's parameter list. Reading those would be a guess.
7057    ///
7058    /// The bytes are still ordinary declarations, though, and the collapse is
7059    /// the parser carrying the failure forward from one macro invocation. So
7060    /// reparse the region, walk the items the parser makes of it, and when one
7061    /// item is itself a collapsed run, recover that invocation from its own
7062    /// bytes and resume the scan just past it. Each pass starts later than the
7063    /// last, so the scan is a loop over the invocations that collapse, not over
7064    /// the declarations: `llama.h` needs three passes for 214 declarations.
7065    ///
7066    /// The region can reach past `envelope` into the siblings the parser handed
7067    /// the rest of one invocation, so it is recorded as consumed: those
7068    /// siblings are this recovery's, and the ordinary walk must not read them
7069    /// again. Recording it after the scan, not before, keeps the scan's own
7070    /// reparsed nodes -- which carry their original byte offsets -- visible to
7071    /// the walk it drives.
7072    fn visit_collapsed_macro_declaration_run<'tree>(
7073        &mut self,
7074        envelope: Node<'tree>,
7075        scope: &ScopeInfo,
7076        ancestry: &ParentIndex<'tree>,
7077    ) -> bool {
7078        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7079            return false;
7080        };
7081        let start = envelope.start_byte();
7082        let end = run.region_end;
7083        let recovery = cpp_recovery_window(self.source, start, end);
7084        self.record_recovered_declarations(recovery, envelope, |visitor| {
7085            let mut position = start;
7086            while position < end {
7087                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7088                    return;
7089                };
7090                let root = tree.root_node();
7091                // A region reparse is its own tree and needs its own parent
7092                // index and dependency arena; the caller's answers nothing
7093                // about these nodes.
7094                let ancestry = visitor.recovery_ancestry(root);
7095                let mut cursor = root.walk();
7096                let collapsed =
7097                    root.named_children(&mut cursor)
7098                        .enumerate()
7099                        .find_map(|(index, item)| {
7100                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7101                                .map(|run| (index, item, run))
7102                        });
7103                // Walk only the items before the collapsed one. It swallowed
7104                // the rest of the region, so handing it to the ordinary walk
7105                // would re-enter this recovery on a region that starts where
7106                // this one did.
7107                let mut stack = Vec::new();
7108                push_cpp_sibling_range(
7109                    root,
7110                    0,
7111                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7112                    scope.clone(),
7113                    &mut stack,
7114                );
7115                visitor.drain_cpp_work(stack, &ancestry);
7116                let Some((_, item, run)) = collapsed else {
7117                    return;
7118                };
7119                // On its own bytes the invocation is the declaration-scope
7120                // shape `macro_wrapped_declarations` reads, so its wrapped
7121                // declaration needs no reader of its own here.
7122                if let Some(head) =
7123                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7124                {
7125                    let head_root = head.root_node();
7126                    let head_ancestry = visitor.recovery_ancestry(head_root);
7127                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7128                }
7129                assert!(
7130                    run.invocation_end > position,
7131                    "a collapsed run at {position} must end after the byte the scan resumed \
7132                     from, but ended at {}",
7133                    run.invocation_end
7134                );
7135                position = run.invocation_end;
7136            }
7137        });
7138        self.consumed_fragment_regions.push((start, end));
7139        true
7140    }
7141
7142    /// Index the members written under an access label that a class-scope
7143    /// macro invocation swallowed. See [`cpp_macro_swallowed_access_label`]
7144    /// for the shape.
7145    ///
7146    /// The members are whole in the source but not in this tree, and at file
7147    /// scope a bodiless member such as `Ctor(int a);` reads as a variable
7148    /// declaration, so the reparse keeps the class around them: the class head
7149    /// through its `{`, the label and its members, and the class's `}`, as one
7150    /// included-range parse at the original offsets. That is the parse the
7151    /// class gets with the invocation's bytes left out, which is what the
7152    /// replacement's own declarations would have to be read without anyway.
7153    /// The reparsed label is a real `access_specifier`, so the members carry the
7154    /// access they are written under. Only the members the label swallowed are
7155    /// walked; the class's other members are the ordinary walk's. When the
7156    /// reparse does not read the label back, nothing is published: the
7157    /// field this node would otherwise mint is named after the access keyword.
7158    fn visit_macro_swallowed_access_label_members<'tree>(
7159        &mut self,
7160        node: Node<'tree>,
7161        label: &CppSwallowedAccessLabel<'tree>,
7162        scope: &ScopeInfo,
7163        ancestry: &ParentIndex<'tree>,
7164    ) {
7165        let Some(body) = ancestry.parent(node) else {
7166            return;
7167        };
7168        let Some(class_like) = ancestry.parent(body) else {
7169            return;
7170        };
7171        // The reparse holds the class itself, so the recovery's source context
7172        // is the class's enclosing one; the class's own scope would name it
7173        // twice in the recovered members' lexical paths.
7174        let Some(class_parent) = ancestry.parent(class_like) else {
7175            return;
7176        };
7177        let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7178            return;
7179        };
7180        if body.kind() != "field_declaration_list"
7181            || open.kind() != "{"
7182            || close.kind() != "}"
7183            || close.is_missing()
7184        {
7185            return;
7186        }
7187        let start = label.keyword.start_byte();
7188        let end = node.end_byte();
7189        let Some(tree) = parse_source_ranges_with_cancellation(
7190            &tree_sitter_cpp::LANGUAGE.into(),
7191            self.source,
7192            &[
7193                (class_like.start_byte(), open.end_byte()),
7194                (start, end),
7195                (close.start_byte(), close.end_byte()),
7196            ],
7197            None,
7198        ) else {
7199            return;
7200        };
7201        let root = tree.root_node();
7202        let Some(reparsed_body) = root
7203            .descendant_for_byte_range(open.start_byte(), open.end_byte())
7204            .and_then(|brace| brace.parent())
7205            .filter(|parent| parent.kind() == "field_declaration_list")
7206        else {
7207            return;
7208        };
7209        let mut cursor = reparsed_body.walk();
7210        let members = reparsed_body
7211            .named_children(&mut cursor)
7212            .enumerate()
7213            .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7214            .map(|(index, member)| (index, member.kind(), member.start_byte()))
7215            .collect::<Vec<_>>();
7216        let Some(&(first, "access_specifier", label_start)) = members.first() else {
7217            return;
7218        };
7219        if label_start != start {
7220            return;
7221        }
7222        let last = members.last().expect("the label is a member").0;
7223        let reparsed_ancestry = self.recovery_ancestry(root);
7224        let recovery = cpp_recovery_window(self.source, start, end);
7225        self.record_recovered_declarations(recovery, class_parent, |visitor| {
7226            let mut stack = Vec::new();
7227            push_cpp_sibling_range(
7228                reparsed_body,
7229                first + 1,
7230                last + 1,
7231                scope.clone(),
7232                &mut stack,
7233            );
7234            visitor.drain_cpp_work(stack, &reparsed_ancestry);
7235        });
7236    }
7237
7238    /// Index the members a string-argument attribute macro stranded in an
7239    /// `ERROR` inside a class body.
7240    ///
7241    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
7242    /// grouping of the attributed member and of every member written after it
7243    /// until it recovers. The members are still whole `function_declarator`
7244    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
7245    /// export-macro class such as Botan's `DL_Group` was indexed with no
7246    /// members at all (#2552).
7247    fn visit_stranded_class_members<'tree>(
7248        &mut self,
7249        node: Node<'tree>,
7250        scope: &ScopeInfo,
7251        ancestry: &ParentIndex<'tree>,
7252    ) {
7253        if scope.class_unit.is_none() || !scope.declarations_are_fields {
7254            return;
7255        }
7256        for member in stranded_declaration_run(node, self.source).declarations {
7257            self.add_macro_wrapped_declaration(member, scope, ancestry);
7258        }
7259    }
7260
7261    /// Index the constructor an access label swallowed in a reparsed class
7262    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
7263    ///
7264    /// The declarator is recovered by reparsing from that call to the end of
7265    /// the label's own statement, which is the same offset-preserving region
7266    /// reparse every other recovery here uses, so the recovered nodes carry
7267    /// their true source positions.
7268    fn visit_access_label_constructor<'tree>(
7269        &mut self,
7270        node: Node<'tree>,
7271        scope: &ScopeInfo,
7272        ancestry: &ParentIndex<'tree>,
7273    ) {
7274        let Some(class_unit) = scope.class_unit.clone() else {
7275            return;
7276        };
7277        if !scope.declarations_are_fields {
7278            return;
7279        }
7280        let class_name = class_unit.identifier().to_string();
7281        // Statement context reads pointer parameters as multiplication. Use the
7282        // same class-context validation as an unlabelled constructor statement.
7283        let statement = node.named_children(&mut node.walk()).find(|child| {
7284            cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7285        });
7286        if let Some(statement) = statement {
7287            self.visit_bare_constructor_expression(statement, scope, ancestry);
7288            return;
7289        }
7290        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7291        else {
7292            return;
7293        };
7294        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7295            return;
7296        };
7297        let root = tree.root_node();
7298        let Some(declarator) =
7299            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7300        else {
7301            return;
7302        };
7303        let reparsed_ancestry = self.recovery_ancestry(root);
7304        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7305        let range = cpp_declaration_range(definition.unwrap_or(declarator));
7306        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7307        self.record_recovered_declarations(recovery, node, |visitor| {
7308            visitor.add_macro_wrapped_declaration(
7309                MacroWrappedDeclaration {
7310                    declarator,
7311                    range,
7312                    is_static: false,
7313                },
7314                scope,
7315                &reparsed_ancestry,
7316            );
7317        });
7318        // This constructor has been accounted for. Its statement-context
7319        // initializer declarators do not declare additional members.
7320        self.consumed_fragment_regions.push((start, range.end_byte));
7321    }
7322
7323    /// Index the members a string-argument attribute macro stranded inside the
7324    /// `macro_type_specifier` the macro-declaration grammar reads them as (#2552
7325    /// shape 3 under the new grammar).
7326    ///
7327    /// `BOTAN_DEPRECATED("text") explicit Ctor(T); Ctor(U, V);` keeps the macro as
7328    /// a `macro_type_specifier` whose argument region is an `ERROR` holding the
7329    /// string, the attributed member as a `type_descriptor`, and its `;`, while
7330    /// the member after it becomes the specifier's own `type` field. Neither
7331    /// member has a real declarator node -- an abstract function declarator in a
7332    /// `type_descriptor` carries only parameters -- but each member's source range
7333    /// is whole, so reparse it and read the declarator from the reparse, which is
7334    /// the same offset-preserving region reparse every other recovery here uses.
7335    /// The admission gate is the same one every reparse shares: exactly one clean
7336    /// callable declaration spanning the member's range.
7337    fn visit_string_attribute_macro_type_members<'tree>(
7338        &mut self,
7339        node: Node<'tree>,
7340        scope: &ScopeInfo,
7341    ) -> bool {
7342        if node.kind() != "field_declaration" {
7343            return false;
7344        }
7345        let Some(specifier) = node
7346            .child_by_field_name("type")
7347            .filter(|type_node| type_node.kind() == "macro_type_specifier")
7348        else {
7349            return false;
7350        };
7351        if specifier
7352            .child_by_field_name("name")
7353            .is_none_or(|name| name.kind() != "identifier")
7354        {
7355            return false;
7356        }
7357        // The attributed member sits inside the argument `ERROR`: the string's
7358        // quote is the first child, the member's `;` the last, and its
7359        // type-descriptor is in between.
7360        let mut cursor = specifier.walk();
7361        let Some(argument_error) = specifier
7362            .named_children(&mut cursor)
7363            .find(|child| child.kind() == "ERROR")
7364        else {
7365            return false;
7366        };
7367        if argument_error
7368            .child(0)
7369            .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7370            || argument_error
7371                .child(argument_error.child_count().saturating_sub(1))
7372                .is_none_or(|last| last.kind() != ";" || last.is_missing())
7373        {
7374            return false;
7375        }
7376        let mut members: Vec<((usize, usize), String)> = Vec::new();
7377        for index in 0..argument_error.child_count() {
7378            let Some(child) = argument_error.child(index) else {
7379                continue;
7380            };
7381            if child.kind() == "type_descriptor" {
7382                let start = (0..index)
7383                    .rev()
7384                    .find_map(|previous| {
7385                        argument_error
7386                            .child(previous)
7387                            .filter(|sibling| sibling.kind() == "explicit")
7388                    })
7389                    .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7390                let Some(name) = child
7391                    .child_by_field_name("type")
7392                    .and_then(|type_node| direct_identifier_name(type_node, self.source))
7393                else {
7394                    return false;
7395                };
7396                members.push(((start, argument_error.end_byte()), name));
7397            }
7398        }
7399        let [(first_range, first_name)] = members.as_slice() else {
7400            return false;
7401        };
7402        let Some(second) = specifier
7403            .child_by_field_name("type")
7404            .filter(|type_node| type_node.kind() == "type_descriptor")
7405        else {
7406            return false;
7407        };
7408        let Some(second_name) = second
7409            .child_by_field_name("type")
7410            .and_then(|type_node| direct_identifier_name(type_node, self.source))
7411        else {
7412            return false;
7413        };
7414        let members = [
7415            (*first_range, first_name.as_str()),
7416            ((second.start_byte(), node.end_byte()), second_name.as_str()),
7417        ];
7418        let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7419        self.record_recovered_declarations(recovery, node, |visitor| {
7420            for ((start, end), member_name) in members {
7421                let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7422                    continue;
7423                };
7424                let root = tree.root_node();
7425                // The file-scope reparse of a member-shaped head is errorful
7426                // (a member qualifier is not valid there), so the admission is
7427                // the declarator itself: exactly one function declarator
7428                // naming the member inside its own range.
7429                let mut candidate = None;
7430                let mut stack = vec![root];
7431                while let Some(current) = stack.pop() {
7432                    if current.kind() == "function_declarator"
7433                        && current.start_byte() >= start
7434                        && current.end_byte() <= end
7435                        && cpp_function_declarator_name_node(current)
7436                            .is_some_and(|name| node_text(name, visitor.source) == member_name)
7437                    {
7438                        if candidate.is_some() {
7439                            candidate = None;
7440                            break;
7441                        }
7442                        candidate = Some(current);
7443                        continue;
7444                    }
7445                    let mut cursor = current.walk();
7446                    stack.extend(current.named_children(&mut cursor));
7447                }
7448                let Some(declarator) = candidate else {
7449                    continue;
7450                };
7451                let reparsed_ancestry = visitor.recovery_ancestry(root);
7452                visitor.add_macro_wrapped_declaration(
7453                    MacroWrappedDeclaration {
7454                        declarator,
7455                        range: cpp_recovery_window(visitor.source, start, end),
7456                        is_static: false,
7457                    },
7458                    scope,
7459                    &reparsed_ancestry,
7460                );
7461            }
7462        });
7463        true
7464    }
7465
7466    fn add_macro_wrapped_declaration<'tree>(
7467        &mut self,
7468        declaration: MacroWrappedDeclaration<'tree>,
7469        scope: &ScopeInfo,
7470        ancestry: &ParentIndex<'tree>,
7471    ) {
7472        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7473        else {
7474            return;
7475        };
7476        let code_unit =
7477            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7478        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7479        let is_constructor = scope
7480            .class_unit
7481            .as_ref()
7482            .is_some_and(|class| function.name == class.identifier())
7483            || cpp_callable_is_structural_constructor(
7484                declaration.declarator,
7485                self.source,
7486                ancestry,
7487            );
7488        let already_declared = self.parsed.contains_declaration(&code_unit);
7489        if already_declared {
7490            self.parsed
7491                .record_navigation_range(code_unit.clone(), declaration.range);
7492            if definition.is_none() {
7493                return;
7494            }
7495            // Preserve the earlier declaration-only occurrence while adding
7496            // the recovered body as a distinct definition occurrence.
7497        }
7498        self.add_declaration_with_range(
7499            ancestry,
7500            code_unit.clone(),
7501            declaration.range,
7502            Some(declaration.declarator),
7503            None,
7504            None,
7505        );
7506        if definition.is_some() {
7507            let recovered_declaration = *self
7508                .source_links
7509                .get(&code_unit)
7510                .and_then(|links| links.last())
7511                .expect("recovered definition source");
7512            let mut properties = self.source_properties.borrow_mut();
7513            let fact = properties
7514                .get_mut(&recovered_declaration)
7515                .expect("recovered definition source fact");
7516            fact.occurrence_role =
7517                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7518            fact.callable_is_constructor |= is_constructor;
7519        }
7520        let signature = normalize_cpp_whitespace(
7521            self.source
7522                .get(declaration.range.start_byte..declaration.range.end_byte)
7523                .expect("a recovered declaration range covers one source range"),
7524        );
7525        let linkage = if declaration.is_static {
7526            CallableLinkage::Internal
7527        } else {
7528            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7529        };
7530        // Whether this is a definition is a property of the recovered node, not
7531        // of the caller: a declarator that a `function_definition` gives a body
7532        // is a definition wherever the recovery found it.
7533        let declaration_only = definition.is_none();
7534        self.add_signature_with_metadata(
7535            code_unit.clone(),
7536            cpp_signature_metadata(
7537                signature,
7538                declaration.declarator,
7539                linkage,
7540                self.source,
7541                ancestry,
7542            )
7543            .with_declaration_only(declaration_only),
7544        );
7545        if already_declared {
7546            return;
7547        }
7548        if let Some(parent) = &scope.class_unit {
7549            self.parsed.add_child(parent.clone(), code_unit);
7550        } else if let Some(module) = &scope.module {
7551            self.parsed.add_child(module.clone(), code_unit);
7552        }
7553    }
7554
7555    /// Index only anonymous aggregate declarations from an ordinary
7556    /// function body. Function bodies are otherwise outside the declaration
7557    /// walk, but their anonymous aggregate owners must exist so structured
7558    /// local binding inference can name their fields (#2994).
7559    fn visit_anonymous_local_aggregates_in_function<'tree>(
7560        &mut self,
7561        function: Node<'tree>,
7562        scope: &ScopeInfo,
7563        stack: &mut Vec<CppWork<'tree>>,
7564        ancestry: &ParentIndex<'tree>,
7565    ) {
7566        let Some(body) = cpp_body_node(function) else {
7567            return;
7568        };
7569        // A body opens a block scope, so nothing declared in it is a member of
7570        // the class the definition may be written inside. Dropping the
7571        // aggregate carrier gives an inline member function's body the same
7572        // block scope an out-of-line `void C::f()` body already has; without
7573        // it a C++ header, where member bodies are usually inline, would keep
7574        // none of these owners (#3301).
7575        let block_scope;
7576        let scope = if scope.class_unit.is_some() {
7577            block_scope = ScopeInfo {
7578                class_unit: None,
7579                declarations_are_fields: false,
7580                ..scope.clone()
7581            };
7582            &block_scope
7583        } else {
7584            scope
7585        };
7586        let mut pending = vec![body];
7587        while let Some(node) = pending.pop() {
7588            if matches!(node.kind(), "function_definition" | "lambda_expression") {
7589                continue;
7590            }
7591            if node.kind() == "declaration"
7592                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7593            {
7594                continue;
7595            }
7596            if matches!(
7597                node.kind(),
7598                "class_specifier" | "struct_specifier" | "union_specifier"
7599            ) {
7600                continue;
7601            }
7602            let mut cursor = node.walk();
7603            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7604            children.reverse();
7605            pending.extend(children);
7606        }
7607    }
7608
7609    fn visit_error_swallowed_function_declaration<'tree>(
7610        &mut self,
7611        node: Node<'tree>,
7612        scope: &ScopeInfo,
7613    ) {
7614        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7615            return;
7616        };
7617        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7618            return;
7619        };
7620        let root = tree.root_node();
7621        let mut cursor = root.walk();
7622        let declarations = root
7623            .named_children(&mut cursor)
7624            .filter(|child| child.kind() != "comment")
7625            .collect::<Vec<_>>();
7626        let [declaration] = declarations.as_slice() else {
7627            return;
7628        };
7629        if declaration.kind() != "declaration"
7630            || declaration.has_error()
7631            || declaration.start_byte() != start
7632            || declaration.end_byte() != end
7633        {
7634            return;
7635        }
7636        let recovery = cpp_recovery_window(self.source, start, end);
7637        // The reparsed region is its own tree, so this walk indexes it itself.
7638        let reparsed_ancestry = self.recovery_ancestry(root);
7639        self.record_recovered_declarations(recovery, node, |visitor| {
7640            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7641        });
7642    }
7643
7644    /// Recover a K&R-compatibility prototype macro invocation such as
7645    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
7646    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
7647    /// defined only in headers outside this workspace (ruby's own
7648    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
7649    /// `name _((args))` and turns each such line into a malformed
7650    /// `declaration` or pointer-expression statement. Before this
7651    /// recovery, the ordinary declaration visitor read that wreckage as a
7652    /// field with the type and name swapped (e.g. a field literally named
7653    /// `VALUE`) and indexed no prototype for the real name at all.
7654    ///
7655    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
7656    /// preserve the declared name, a known one-argument compatibility macro,
7657    /// the nested argument list, and a live terminator. It rejects arbitrary
7658    /// identifiers and bare `ERROR` envelopes that flattened those relations;
7659    /// guessing there previously turned malformed non-macro declarations into
7660    /// invented functions. For each proven candidate this reparses the range
7661    /// before the macro token, the inner parenthesized argument node, and the
7662    /// terminating `;` as one included-range parse. That is the parser-native
7663    /// equivalent of expanding `#define _(args) args` while retaining original
7664    /// byte offsets. The result is admitted only when it is exactly one clean
7665    /// function prototype spanning the recovered run.
7666    ///
7667    /// A class-body line is a `field_declaration`, and a bodiless member read
7668    /// at file scope loses the class-body position its `static` and access
7669    /// facts depend on. That reparse therefore keeps the class around the
7670    /// prototype, as [`PrototypeMacroClassEnvelope`] describes, and admits
7671    /// exactly one clean member prototype in the reparsed body instead.
7672    ///
7673    /// Returns whether a candidate was recovered.
7674    fn visit_prototype_macro_declarations<'tree>(
7675        &mut self,
7676        node: Node<'tree>,
7677        scope: &ScopeInfo,
7678        ancestry: &ParentIndex<'tree>,
7679    ) -> bool {
7680        let candidates = cpp_prototype_macro_candidates(node, self.source);
7681        if candidates.is_empty() {
7682            return false;
7683        }
7684        let envelope = if node.kind() == "field_declaration" {
7685            let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7686            else {
7687                return false;
7688            };
7689            Some(envelope)
7690        } else {
7691            None
7692        };
7693        let mut recovered = false;
7694        for candidate in candidates {
7695            let start = candidate.run_start;
7696            let end = candidate.semicolon_end;
7697            if self.byte_range_is_inside_consumed_fragment(start, end) {
7698                continue;
7699            }
7700            let mut ranges = Vec::with_capacity(6);
7701            if let Some(envelope) = &envelope {
7702                ranges.push(envelope.head);
7703                ranges.extend(envelope.label);
7704            }
7705            ranges.extend(candidate.ranges());
7706            if let Some(envelope) = &envelope {
7707                ranges.push(envelope.close);
7708            }
7709            let Some(tree) = parse_source_ranges_with_cancellation(
7710                &tree_sitter_cpp::LANGUAGE.into(),
7711                self.source,
7712                &ranges,
7713                None,
7714            ) else {
7715                continue;
7716            };
7717            let root = tree.root_node();
7718            let (container, expected_kind) = match &envelope {
7719                None => (root, "declaration"),
7720                Some(envelope) => {
7721                    let Some(body) = root
7722                        .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7723                        .and_then(|brace| brace.parent())
7724                        .filter(|parent| parent.kind() == "field_declaration_list")
7725                    else {
7726                        continue;
7727                    };
7728                    (body, "field_declaration")
7729                }
7730            };
7731            // Everything the envelope contributes ends before the prototype.
7732            let mut cursor = container.walk();
7733            let declarations = container
7734                .named_children(&mut cursor)
7735                .enumerate()
7736                .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7737                .collect::<Vec<_>>();
7738            let [(index, declaration)] = declarations.as_slice() else {
7739                continue;
7740            };
7741            if declaration.kind() != expected_kind
7742                || declaration.has_error()
7743                || declaration.start_byte() != start
7744                || declaration.end_byte() != end
7745                || declaration
7746                    .child_by_field_name("declarator")
7747                    .and_then(extract_function_declarator)
7748                    .is_none()
7749            {
7750                continue;
7751            }
7752            let index = *index;
7753            let recovery = cpp_recovery_window(self.source, start, end);
7754            // The reparsed region is its own tree, so this walk indexes it
7755            // itself. A class envelope holds the class, so the recovery's
7756            // source context is the class's enclosing one; the class's own
7757            // would name it twice in the member's lexical path.
7758            let reparsed_ancestry = self.recovery_ancestry(root);
7759            let origin = envelope
7760                .as_ref()
7761                .map_or(node, |envelope| envelope.class_parent);
7762            self.record_recovered_declarations(recovery, origin, |visitor| {
7763                let mut stack = Vec::new();
7764                push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7765                visitor.drain_cpp_work(stack, &reparsed_ancestry);
7766            });
7767            self.consumed_fragment_regions.push((start, end));
7768            recovered = true;
7769        }
7770        recovered
7771    }
7772
7773    /// Declare one Module per namespace level of `levels` under `package_name`,
7774    /// each recording the range of the construct that writes it, and return the
7775    /// innermost level's package name and Module. A level an earlier definition
7776    /// already declared is reused, which is also how a reopened namespace keeps
7777    /// the range of the definition that first declared it.
7778    fn declare_namespace_levels(
7779        &mut self,
7780        mut package_name: String,
7781        levels: Vec<(String, Range, Option<Range>)>,
7782        node: Node<'_>,
7783        ancestry: &ParentIndex<'_>,
7784    ) -> (String, Option<CodeUnit>) {
7785        let mut module = None;
7786        for (component, range, name_range) in levels {
7787            let full_name = if package_name.is_empty() {
7788                component
7789            } else {
7790                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7791            };
7792            let level = CodeUnit::new_fq(
7793                self.file.clone(),
7794                CodeUnitType::Module,
7795                "",
7796                full_name.clone(),
7797                cpp_namespace_fq(&full_name),
7798            );
7799            if !self.parsed.contains_declaration(&level) {
7800                if let Some(name_range) = name_range {
7801                    self.add_declaration_with_name_range(
7802                        level.clone(),
7803                        range,
7804                        name_range,
7805                        Some((ancestry, node)),
7806                        None,
7807                        None,
7808                    );
7809                } else {
7810                    self.add_declaration_with_range(
7811                        ancestry,
7812                        level.clone(),
7813                        range,
7814                        Some(node),
7815                        None,
7816                        None,
7817                    );
7818                }
7819            }
7820            package_name = full_name;
7821            module = Some(level);
7822        }
7823        (package_name, module)
7824    }
7825
7826    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
7827    /// index restores. Each level carries the range of its own name token,
7828    /// which the walk's current node cannot supply: that node is whichever
7829    /// declaration the walk reached first inside the region, and deriving a
7830    /// level's name from it paired the level's range with an unrelated
7831    /// construct's name token (#4292).
7832    fn declare_recovered_namespace_levels(
7833        &mut self,
7834        mut package_name: String,
7835        levels: Vec<(String, Range, Range)>,
7836    ) -> (String, Option<CodeUnit>) {
7837        let mut module = None;
7838        for (component, range, name_range) in levels {
7839            let full_name = if package_name.is_empty() {
7840                component
7841            } else {
7842                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7843            };
7844            let level = CodeUnit::new_fq(
7845                self.file.clone(),
7846                CodeUnitType::Module,
7847                "",
7848                full_name.clone(),
7849                cpp_namespace_fq(&full_name),
7850            );
7851            if !self.parsed.contains_declaration(&level) {
7852                self.add_declaration_with_name_range(
7853                    level.clone(),
7854                    range,
7855                    name_range,
7856                    None,
7857                    None,
7858                    None,
7859                );
7860            }
7861            package_name = full_name;
7862            module = Some(level);
7863        }
7864        (package_name, module)
7865    }
7866
7867    /// Restore the complete lexical namespace path when recovery dropped an
7868    /// ancestor or retained it past its real close. Parsed namespaces beginning
7869    /// inside the recovered region still contribute their normal nesting.
7870    /// Crossing that boundary also clears any malformed class owner.
7871    fn recovered_namespace_scope(
7872        &mut self,
7873        node: Node<'_>,
7874        scope: &ScopeInfo,
7875    ) -> Option<ScopeInfo> {
7876        self.orphaned_namespaces.region_at(node.start_byte())?;
7877        // Each level carries the `namespace Name { ... }` construct that writes
7878        // it along with the level's own name token. Declaring them at `node`
7879        // instead recorded the range of whichever member the walk happened to
7880        // reach first, so a namespace whose head collapsed into an ERROR
7881        // reported its first inner declaration's lines as its own (#3309), and
7882        // deriving the name from that node paired the level's range with the
7883        // member's name token (#4292).
7884        let levels = self
7885            .orphaned_namespaces
7886            .enclosing_namespace_levels(node, self.source);
7887        let package_name = levels
7888            .iter()
7889            .map(|(component, ..)| component.as_str())
7890            .collect::<Vec<_>>()
7891            .join(CPP_PACKAGE_SEPARATOR);
7892        if package_name == scope.package_name {
7893            return None;
7894        }
7895        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7896        Some(ScopeInfo {
7897            package_name,
7898            module,
7899            // A recovered namespace body is no longer inside the class (or
7900            // function) that tree-sitter accidentally used as its wrapper.
7901            // Keep namespace/module context, but never carry that owner into
7902            // declarations published from the recovered region.
7903            class_unit: None,
7904            template_signature: None,
7905            template_metadata: None,
7906            declarations_are_fields: false,
7907            recovered_specialization_member_scope: false,
7908            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7909            visible_using_declarations: scope.visible_using_declarations.clone(),
7910        })
7911    }
7912
7913    fn visit_namespace<'tree>(
7914        &mut self,
7915        node: Node<'tree>,
7916        scope: &ScopeInfo,
7917        stack: &mut Vec<CppWork<'tree>>,
7918        ancestry: &ParentIndex<'tree>,
7919    ) {
7920        let name_node = node.child_by_field_name("name");
7921        let Some(name_node) = name_node else {
7922            if let Some(body) = cpp_body_node(node) {
7923                stack.push(CppWork::Container(CppContainer {
7924                    node: body,
7925                    scope: scope.clone(),
7926                }));
7927            }
7928            return;
7929        };
7930        // Diagnostic corpora contain deliberately ill-formed global namespace
7931        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
7932        // the leading global `::` as the first anonymous child. Honor that AST
7933        // boundary instead of appending the name to the lexical namespace;
7934        // appending produced legacy names such as `outer::::outer::inner`, which
7935        // could not round-trip through the structured FqName boundary.
7936        let explicitly_global = name_node
7937            .child(0)
7938            .is_some_and(|child| !child.is_named() && child.kind() == "::");
7939        let components = cpp_namespace_name_components(name_node, self.source);
7940        if components.is_empty() {
7941            return;
7942        }
7943        // One Module per namespace level. C++17's `namespace a::b { ... }` is
7944        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
7945        // shorthand must declare `a` as well as `a::b` -- extracting only the
7946        // innermost level left the enclosing namespace undeclared and made the
7947        // two spellings of one construct disagree (issue #1878).
7948        let package_name = if explicitly_global {
7949            String::new()
7950        } else {
7951            scope.package_name.clone()
7952        };
7953        let definition = node_range(node);
7954        // The `namespace a::b` shorthand names every level in one node: each
7955        // level's name occurrence is its own token, the same evidence the
7956        // recovered-namespace path carries per level (#4292).
7957        let mut component_ranges =
7958            cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
7959        let (package_name, module) = self.declare_namespace_levels(
7960            package_name,
7961            components
7962                .into_iter()
7963                .map(|component| {
7964                    let name_range = component_ranges
7965                        .next()
7966                        .filter(|(name, _)| *name == component)
7967                        .map(|(_, range)| range);
7968                    (component, definition, name_range)
7969                })
7970                .collect(),
7971            node,
7972            ancestry,
7973        );
7974
7975        let namespace_scope = ScopeInfo {
7976            package_name,
7977            module,
7978            // C++ never nests a namespace inside a class, so a surviving
7979            // class_unit here is always recovery bleed: a malformed-region
7980            // boundary upstream mis-scoped this namespace block. Keeping the
7981            // owner would mint the namespace's declarations as class members
7982            // under a re-appended package, desyncing the fq boundary assert
7983            // (#2306). Dropping it is identity-neutral for valid code, where
7984            // class_unit is always empty at a namespace definition.
7985            class_unit: None,
7986            template_signature: scope.template_signature.clone(),
7987            template_metadata: scope.template_metadata.clone(),
7988            declarations_are_fields: false,
7989            recovered_specialization_member_scope: false,
7990            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7991            visible_using_declarations: scope.visible_using_declarations.clone(),
7992        };
7993        let container = cpp_body_node(node).unwrap_or(node);
7994        // A malformed export-macro class body may turn the following class
7995        // into a descendant of a bogus function/labeled/error envelope. Those
7996        // descendants are not declaration containers and the ordinary walk
7997        // intentionally does not descend into them. Scan the namespace tree
7998        // once for the strict embedded class geometry before scheduling its
7999        // normal declarations. When one envelope matches, its helper recovers
8000        // every embedded class and the walk need not inspect its descendants.
8001        let mut candidates = vec![container];
8002        while let Some(candidate) = candidates.pop() {
8003            if matches!(
8004                candidate.kind(),
8005                "ERROR" | "function_definition" | "labeled_statement"
8006            ) && self.visit_embedded_function_like_export_classes(
8007                candidate,
8008                &namespace_scope,
8009                stack,
8010                ancestry,
8011            ) {
8012                continue;
8013            }
8014            for index in (0..candidate.named_child_count()).rev() {
8015                candidates.push(
8016                    candidate
8017                        .named_child(index)
8018                        .expect("index below the node's own named child count"),
8019                );
8020            }
8021        }
8022        stack.push(CppWork::Container(CppContainer {
8023            node: container,
8024            scope: namespace_scope,
8025        }));
8026    }
8027
8028    fn visit_class_like<'tree>(
8029        &mut self,
8030        node: Node<'tree>,
8031        scope: &ScopeInfo,
8032        stack: &mut Vec<CppWork<'tree>>,
8033        ancestry: &ParentIndex<'tree>,
8034    ) {
8035        let Some(name) = class_like_name(node, self.source, ancestry) else {
8036            return;
8037        };
8038        let name = qualified_class_name_chain(node, self.source, scope)
8039            .map(|chain| chain.join("$"))
8040            .unwrap_or(name);
8041        self.visit_named_class_like(node, name, scope, stack, ancestry);
8042    }
8043
8044    fn visit_named_class_like<'tree>(
8045        &mut self,
8046        node: Node<'tree>,
8047        name: String,
8048        scope: &ScopeInfo,
8049        stack: &mut Vec<CppWork<'tree>>,
8050        ancestry: &ParentIndex<'tree>,
8051    ) {
8052        let body = cpp_body_node(node);
8053        let definition_body_present = body.is_some();
8054        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8055            .then(|| extract_cpp_supertypes(node, self.source));
8056        self.visit_named_class_like_shape(
8057            node,
8058            name,
8059            body,
8060            definition_body_present,
8061            None,
8062            raw_supertypes,
8063            Some(extract_cpp_base_facts(node, self.source)),
8064            scope,
8065            stack,
8066            ancestry,
8067        );
8068    }
8069
8070    /// Whether this class-like declaration is a C tag that belongs to the
8071    /// enclosing non-aggregate scope rather than to the aggregate it is
8072    /// lexically written inside.
8073    ///
8074    /// `class_specifier` is deliberately excluded: `class` is not C, so text
8075    /// that spells one in a `.c` file is not C code and keeps the C++ reading
8076    /// rather than getting a half-C identity.
8077    fn mints_tag_at_enclosing_c_scope(
8078        &self,
8079        declaration_node: Node<'_>,
8080        scope: &ScopeInfo,
8081        ancestry: &ParentIndex<'_>,
8082    ) -> bool {
8083        if scope.class_unit.is_none()
8084            || !matches!(
8085                declaration_node.kind(),
8086                "struct_specifier" | "union_specifier" | "enum_specifier"
8087            )
8088            || class_like_name(declaration_node, self.source, ancestry).is_none()
8089        {
8090            return false;
8091        }
8092        // A class-specifier ancestor proves this is C++ syntax. A C reading of
8093        // the header must not apply C tag hoisting to structs nested in that
8094        // class, because that invents a file-scope identity for its members.
8095        let mut ancestor = ancestry.parent(declaration_node);
8096        while let Some(current) = ancestor {
8097            if current.kind() == "class_specifier" {
8098                return false;
8099            }
8100            ancestor = ancestry.parent(current);
8101        }
8102        true
8103    }
8104
8105    #[allow(clippy::too_many_arguments)]
8106    fn visit_named_class_like_shape<'tree>(
8107        &mut self,
8108        declaration_node: Node<'tree>,
8109        name: String,
8110        body: Option<Node<'tree>>,
8111        definition_body_present: bool,
8112        explicit_range: Option<Range>,
8113        raw_supertypes: Option<Vec<String>>,
8114        base_facts: Option<Vec<CppBaseSpecifierFact>>,
8115        scope: &ScopeInfo,
8116        stack: &mut Vec<CppWork<'tree>>,
8117        ancestry: &ParentIndex<'tree>,
8118    ) -> CodeUnit {
8119        let displaced_macro_tail = if explicit_range.is_none() {
8120            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8121        } else {
8122            None
8123        };
8124        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8125        let recovered_scope = self.scope_for_recovered_exported_class(
8126            declaration_node,
8127            &name,
8128            definition_body_present,
8129            scope,
8130            ancestry,
8131        );
8132        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
8133        // aggregate's member list is declared at the enclosing non-aggregate
8134        // scope, not nested inside the aggregate. `scope.class_unit` is the
8135        // only aggregate carrier in this walk, so dropping it puts the tag at
8136        // the nearest enclosing non-aggregate scope -- the module at file or
8137        // namespace scope, and the same block-scope representation a
8138        // function-local aggregate already gets. The tag's own body scope
8139        // below still owns its members, so fields and enumerators are
8140        // unaffected.
8141        let c_tag_scope;
8142        let c_tag_scope_applies =
8143            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8144        if !self.c_tag_semantics && c_tag_scope_applies {
8145            self.c_tag_scope_witness = true;
8146        }
8147        let scope = if self.c_tag_semantics && c_tag_scope_applies {
8148            c_tag_scope = ScopeInfo {
8149                class_unit: None,
8150                ..recovered_scope.clone()
8151            };
8152            &c_tag_scope
8153        } else {
8154            &recovered_scope
8155        };
8156        let short_name = if let Some(parent) = &scope.class_unit {
8157            cpp_join_nested_short(parent.short_name(), &name)
8158        } else {
8159            name.clone()
8160        };
8161        // A top-level out-of-line qualified class definition (`struct
8162        // Outer::Inner { ... }` inside its namespace, #2246) carries its
8163        // nesting chain as the `$`-joined display name; push one Type/Nested
8164        // segment per class so segment-pop owner navigation keeps working.
8165        // Every other leaf name stays opaque so a literal `$` in a source
8166        // identifier never crosses the split/join boundary (#2140).
8167        let qualified_chain = if scope.class_unit.is_none() {
8168            qualified_class_name_chain(declaration_node, self.source, scope)
8169                .filter(|chain| chain.join("$") == name)
8170        } else {
8171            None
8172        };
8173        let fq = if let Some(chain) = qualified_chain {
8174            let mut fq = FqName::new();
8175            cpp_push_package(&mut fq, &scope.package_name);
8176            let mut first = true;
8177            for component in chain {
8178                let kind = if first {
8179                    SegmentKind::Type
8180                } else {
8181                    SegmentKind::Nested
8182                };
8183                fq.push(segment_interner().intern(&component, kind));
8184                first = false;
8185            }
8186            fq
8187        } else {
8188            cpp_leaf_fq(
8189                &scope.package_name,
8190                scope.class_unit.as_ref(),
8191                &name,
8192                SegmentKind::Nested,
8193                SegmentKind::Type,
8194            )
8195        };
8196        let code_unit = CodeUnit::with_signature_and_fq(
8197            self.file.clone(),
8198            CodeUnitType::Class,
8199            scope.package_name.clone(),
8200            short_name,
8201            scope.template_signature.clone(),
8202            false,
8203            fq,
8204        );
8205        let has_body = definition_body_present;
8206        if !has_body && self.parsed.contains_declaration(&code_unit) {
8207            if let Some(range) = explicit_range {
8208                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8209            } else {
8210                self.link_source_declaration(ancestry, &code_unit, declaration_node);
8211            }
8212            self.parsed.record_navigation_range(
8213                code_unit.clone(),
8214                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8215            );
8216            return code_unit;
8217        }
8218        if has_body {
8219            // A forward declaration above this definition is one of the
8220            // class's declaration sites: C++ injects the class name at the
8221            // forward declaration, so a reference between the two spells this
8222            // class (#3297). Moving the unit to its body must not drop the
8223            // earlier site, exactly as a function prototype and its body stay
8224            // two ranges of one declaration (#1650).
8225            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8226            if let Some(range) = explicit_range {
8227                self.replace_declaration_with_range_deferred(
8228                    ancestry,
8229                    code_unit.clone(),
8230                    range,
8231                    Some(declaration_node),
8232                    None,
8233                    None,
8234                );
8235            } else {
8236                self.replace_declaration_deferred(
8237                    ancestry,
8238                    code_unit.clone(),
8239                    declaration_node,
8240                    None,
8241                    None,
8242                );
8243            }
8244            for range in earlier_ranges {
8245                self.parsed.add_declaration_range(&code_unit, range);
8246            }
8247        } else {
8248            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8249        }
8250        if let Some(declaration) = self
8251            .source_links
8252            .get(&code_unit)
8253            .and_then(|links| links.last())
8254            .copied()
8255        {
8256            let mut properties = self.source_properties.borrow_mut();
8257            let fact = properties
8258                .get_mut(&declaration)
8259                .expect("linked class has source properties");
8260            fact.occurrence_role = if has_body {
8261                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8262            } else {
8263                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8264            };
8265            fact.class_strength = if has_body {
8266                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8267            } else {
8268                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8269            };
8270            if let Some(base_facts) = base_facts.as_ref() {
8271                fact.bases = base_facts.clone();
8272            }
8273        }
8274        if let Some(raw_supertypes) = raw_supertypes {
8275            self.parsed
8276                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8277        }
8278        self.parsed.add_signature(
8279            code_unit.clone(),
8280            render_cpp_type_signature(
8281                declaration_node,
8282                self.source,
8283                scope.template_signature.as_deref(),
8284            ),
8285        );
8286        if let Some(metadata) = &scope.template_metadata {
8287            let primary_short_name = if let Some(parent) = &scope.class_unit {
8288                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8289            } else {
8290                metadata.primary_name.clone()
8291            };
8292            let primary_fq_name = CodeUnit::new(
8293                self.file.clone(),
8294                CodeUnitType::Class,
8295                scope.package_name.clone(),
8296                primary_short_name,
8297            )
8298            .fq_name();
8299            let mut metadata = metadata.clone();
8300            metadata.primary_fq_name = primary_fq_name;
8301            self.parsed
8302                .set_cpp_template_metadata(code_unit.clone(), metadata);
8303        }
8304        if let Some(parent) = &scope.class_unit {
8305            self.parsed.add_child(parent.clone(), code_unit.clone());
8306        } else if let Some(module) = &scope.module {
8307            self.parsed.add_child(module.clone(), code_unit.clone());
8308        }
8309
8310        if let Some(body) = body {
8311            let mut nested_scope = scope.clone();
8312            nested_scope.class_unit = Some(code_unit.clone());
8313            nested_scope.template_signature = scope.template_signature.clone();
8314            // Template metadata describes the class just created. It must not
8315            // leak into ordinary nested declarations in that class's body.
8316            // Recovered export-macro specializations carry a separate scope bit
8317            // for their declaration-shaped body members.
8318            nested_scope.template_metadata = None;
8319            // Export-macro class bodies recovered from a function_definition use
8320            // compound_statement children, whose direct fields are declarations.
8321            nested_scope.recovered_specialization_member_scope =
8322                scope.template_metadata.as_ref().is_some_and(|metadata| {
8323                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
8324                });
8325            nested_scope.declarations_are_fields =
8326                is_recovered_exported_class_container(declaration_node, self.source)
8327                    || nested_scope.recovered_specialization_member_scope;
8328            if let Some(displaced) = displaced_macro_tail {
8329                // A macro-shaped field without a source semicolon can make
8330                // tree-sitter consume the real class terminator as an ERROR
8331                // inside that field, then retain following namespace items as
8332                // later field-list children. Drain the proven class prefix
8333                // first and re-own only the structured tail with the outer
8334                // scope. The tail is pushed first because the work stack is
8335                // LIFO.
8336                push_cpp_sibling_range(
8337                    body,
8338                    displaced.split_index,
8339                    usize::MAX,
8340                    scope.clone(),
8341                    stack,
8342                );
8343                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8344            } else {
8345                stack.push(CppWork::Container(CppContainer {
8346                    node: body,
8347                    scope: nested_scope,
8348                }));
8349            }
8350        }
8351        if declaration_node.kind() == "enum_specifier" {
8352            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8353        }
8354        code_unit
8355    }
8356
8357    fn visit_enum_enumerators(
8358        &mut self,
8359        ancestry: &ParentIndex<'_>,
8360        node: Node<'_>,
8361        scope: &ScopeInfo,
8362        parent: &CodeUnit,
8363    ) {
8364        walk_named_tree_preorder(node, false, |child| {
8365            if child.kind() != "enumerator" {
8366                return WalkControl::Continue;
8367            }
8368            let Some(name_node) = child.child_by_field_name("name") else {
8369                return WalkControl::Continue;
8370            };
8371            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8372            if name.is_empty() {
8373                return WalkControl::Continue;
8374            }
8375            let signature = normalize_cpp_whitespace(node_text(child, self.source));
8376            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8377            WalkControl::Continue
8378        });
8379    }
8380
8381    fn visit_enum_enumerator(
8382        &mut self,
8383        ancestry: &ParentIndex<'_>,
8384        declaration_node: Node<'_>,
8385        scope: &ScopeInfo,
8386        parent: &CodeUnit,
8387        name: &str,
8388        signature: &str,
8389    ) {
8390        let code_unit = CodeUnit::new_fq(
8391            self.file.clone(),
8392            CodeUnitType::Field,
8393            scope.package_name.clone(),
8394            cpp_join_member_short(parent.short_name(), name),
8395            parent
8396                .fq()
8397                .clone()
8398                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8399        );
8400        if self.parsed.contains_declaration(&code_unit) {
8401            return;
8402        }
8403        self.add_declaration(
8404            ancestry,
8405            code_unit.clone(),
8406            declaration_node,
8407            Some(parent.clone()),
8408            None,
8409        );
8410        self.parsed.add_signature(code_unit, signature.to_string());
8411    }
8412
8413    fn visit_function_definition<'tree>(
8414        &mut self,
8415        node: Node<'tree>,
8416        scope: &ScopeInfo,
8417        stack: &mut Vec<CppWork<'tree>>,
8418        ancestry: &ParentIndex<'tree>,
8419    ) {
8420        // An attribute-like macro invocation whose argument is a declaration can
8421        // swallow every declaration written after it into one bogus
8422        // `function_definition` (#2551). This owns the whole region, so it runs
8423        // ahead of `visit_macro_swallowed_function_declarations` below, which
8424        // admits the same envelope by its head macro token and reads single
8425        // declarators out of nodes this recovery reparses properly.
8426        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8427            return;
8428        }
8429        // A file-scope object-like macro sentinel the parser cannot see (issue
8430        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
8431        // prefixes as a bogus `function_definition` that swallows real namespaces,
8432        // classes, and members. Reparse the swallowed interior as C++ items so the
8433        // ordinary declaration visitors index it with byte/line-exact ownership.
8434        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8435            return;
8436        }
8437        // The omitted-semicolon field near miss (`DWORD preserved_field` ahead
8438        // of `void PreserveFollowingFunction() {}`): the macro-declaration
8439        // grammar folds the pair into the function's head as two leading
8440        // macro_modifiers where the older grammar left a damaged
8441        // field_declaration. Index the field; the function itself continues
8442        // through the ordinary path below.
8443        if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8444            self.visit_omitted_semicolon_macro_field(
8445                node,
8446                type_modifier,
8447                name_modifier,
8448                scope,
8449                ancestry,
8450            );
8451        }
8452        // The macro-return stand-in (`CUTLASS_DEVICE static OperandLayout
8453        // Layout() { ... }`): the same clean envelope, but the return type
8454        // names an active template type parameter, and the older grammar's
8455        // phantom field was the definition `OperandLayout::packed` resolved to
8456        // (#1129). Recover that field from the clean head.
8457        if scope.class_unit.is_some()
8458            && let Some((head_start, type_node)) =
8459                macro_return_template_parameter_field(node, self.source, ancestry)
8460        {
8461            self.visit_macro_return_template_parameter_field(
8462                node, head_start, type_node, scope, ancestry,
8463            );
8464        }
8465        if node.has_error() {
8466            self.visit_macro_swallowed_function_declarations(node, scope);
8467        }
8468        if let Some((class_node, name, raw_supertypes, base_facts)) =
8469            recover_exported_class_function_definition(node, self.source)
8470        {
8471            if let Some(body) = cpp_body_node(node)
8472                && let Some(close) = self
8473                    .orphaned_namespaces
8474                    .matching_close_brace(body.start_byte())
8475                && close.end_byte < body.end_byte()
8476            {
8477                let class_range = Range {
8478                    start_byte: node.start_byte(),
8479                    end_byte: close.end_byte,
8480                    start_line: node.start_position().row + 1,
8481                    end_line: close.end_line,
8482                };
8483                // Parse the body in class context, preserving the original
8484                // keyword, recovered name and body as included ranges. Parsing
8485                // the interior at file scope turns constructors and access
8486                // labels into another oversized ERROR instead of members.
8487                let mut head = vec![node];
8488                let mut keyword = None;
8489                let mut name_node = None;
8490                while let Some(part) = head.pop() {
8491                    if part.start_byte() >= body.start_byte() || part.is_missing() {
8492                        continue;
8493                    }
8494                    if matches!(part.kind(), "class" | "struct" | "union") {
8495                        keyword = Some(part);
8496                    }
8497                    if matches!(part.kind(), "identifier" | "type_identifier")
8498                        && node_text(part, self.source) == name
8499                    {
8500                        name_node = Some(part);
8501                    }
8502                    let mut cursor = part.walk();
8503                    head.extend(part.children(&mut cursor));
8504                }
8505                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8506                    && let Some(type_name) = keyword
8507                        .parent()
8508                        .and_then(|parent| parent.child_by_field_name("name"))
8509                    && let Some(tree) = parse_source_ranges_with_cancellation(
8510                        &tree_sitter_cpp::LANGUAGE.into(),
8511                        self.source,
8512                        &[
8513                            (keyword.start_byte(), type_name.start_byte()),
8514                            (name_node.start_byte(), name_node.end_byte()),
8515                            (body.start_byte(), close.end_byte),
8516                        ],
8517                        None,
8518                    )
8519                    && let Some(reparsed_class) = tree.root_node().named_child(0)
8520                    && let Some(class_body) = cpp_body_node(reparsed_class)
8521                    && class_body.start_byte() == body.start_byte()
8522                    && class_body.end_byte() == close.end_byte
8523                    && let Some(tail) =
8524                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8525                {
8526                    let class_body_range = class_body.byte_range();
8527                    self.with_recovery_source_context(node, |visitor| {
8528                        let source_context = visitor.active_source_context.clone();
8529                        let class_unit = visitor.visit_named_class_like_shape(
8530                            class_node,
8531                            name,
8532                            None,
8533                            true,
8534                            Some(class_range),
8535                            raw_supertypes,
8536                            base_facts,
8537                            scope,
8538                            stack,
8539                            ancestry,
8540                        );
8541                        visitor.parsed.record_materialization(
8542                            MaterializationRecord::RecoveredDeclaration {
8543                                recovery: class_range,
8544                                unit: class_unit.clone(),
8545                            },
8546                        );
8547                        let member_scope = ScopeInfo {
8548                            package_name: class_unit.package_name().to_string(),
8549                            class_unit: Some(class_unit),
8550                            declarations_are_fields: true,
8551                            template_metadata: None,
8552                            recovered_specialization_member_scope: false,
8553                            ..scope.clone()
8554                        };
8555                        let tail_range = tail.root_node().byte_range();
8556                        let class_body_recovery = cpp_recovery_window(
8557                            visitor.source,
8558                            class_body_range.start,
8559                            class_body_range.end,
8560                        );
8561                        let tail_recovery =
8562                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8563                        visitor.partitioned_regions.push(CppPartitionedRegion {
8564                            tree: tail,
8565                            range: tail_range,
8566                            scope: scope.clone(),
8567                            recovery: tail_recovery,
8568                            source_context: source_context.clone(),
8569                        });
8570                        visitor.partitioned_regions.push(CppPartitionedRegion {
8571                            tree,
8572                            range: class_body_range,
8573                            scope: member_scope,
8574                            recovery: class_body_recovery,
8575                            source_context: source_context.clone(),
8576                        });
8577                    });
8578                    return;
8579                }
8580            }
8581            let body = cpp_body_node(class_node);
8582            let displaced_namespace = cpp_body_node(node)
8583                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8584            let fragmented = cpp_body_node(node).and_then(|body| {
8585                fragmented_export_function_body_region(
8586                    node,
8587                    body,
8588                    self.source,
8589                    displaced_namespace.as_ref(),
8590                )
8591            });
8592            // The recovery tuple's first node is the class-like type when the
8593            // parser exposes one, but the synthetic wrapper owns the compound
8594            // statement that contains the truncated class body. Use the
8595            // wrapper body for fragmented-member detection; retain the
8596            // class-node body for the ordinary (non-fragmented) path below.
8597            if let Some(fragmented) = fragmented {
8598                // The lifted sibling no longer sits below the parser-visible
8599                // namespace node. Restore the current parent scope when the
8600                // ordinary work walk reaches that class.
8601                if let Some(boundary) =
8602                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8603                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8604                {
8605                    let mut boundary_scope = scope.clone();
8606                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8607                        if sibling.start_byte() >= boundary.start_byte() {
8608                            break;
8609                        }
8610                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8611                            boundary_scope.visible_using_namespaces.push(namespace);
8612                        }
8613                        push_cpp_using_declaration_binding(
8614                            &mut boundary_scope,
8615                            sibling,
8616                            self.source,
8617                        );
8618                    }
8619                    self.recovered_class_sibling_scopes
8620                        .insert(boundary.id(), boundary_scope);
8621                }
8622                let mut recovered_constructor = None;
8623                let mut recovered_prefix_tree = None;
8624                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8625                {
8626                    Some(FragmentedExportMembers::Complete(tree)) => {
8627                        if let Some(body) = body
8628                            && let Some(range) =
8629                                cpp_reparsed_synthetic_initializer_constructor_range(
8630                                    tree.root_node(),
8631                                    &name,
8632                                    self.source,
8633                                    body.end_byte(),
8634                                )
8635                        {
8636                            recovered_constructor = Some(range);
8637                            recovered_prefix_tree = Some(tree);
8638                            None
8639                        } else {
8640                            Some(FragmentedExportMembers::Complete(tree))
8641                        }
8642                    }
8643                    outcome => outcome,
8644                };
8645                let mut class_stack = Vec::new();
8646                let class_unit = self.visit_named_class_like_shape(
8647                    class_node,
8648                    name,
8649                    None,
8650                    true,
8651                    Some(fragmented.class_range),
8652                    raw_supertypes,
8653                    base_facts.clone(),
8654                    scope,
8655                    &mut class_stack,
8656                    ancestry,
8657                );
8658                self.parsed
8659                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8660                        recovery: fragmented.class_range,
8661                        unit: class_unit.clone(),
8662                    });
8663                let complete = outcome.is_some_and(|outcome| {
8664                    self.visit_fragmented_export_class_members(
8665                        node,
8666                        outcome,
8667                        class_unit.clone(),
8668                        scope,
8669                    )
8670                });
8671                if complete {
8672                    self.consumed_fragment_regions
8673                        .push((node.start_byte(), fragmented.class_range.end_byte));
8674                } else {
8675                    // The reparse can fail when the first constructor or a
8676                    // method body is split into statement-shaped siblings.
8677                    // Keep the recovered class envelope, but do not visit the
8678                    // synthetic wrapper body: its initializer expressions can
8679                    // look like same-named member functions (for example
8680                    // `Token.location(loc)`). Re-own only the original sibling
8681                    // nodes that fall inside the proven class range. Their CST
8682                    // shapes retain the real field/function kinds and ranges.
8683                    let member_scope = ScopeInfo {
8684                        package_name: class_unit.package_name().to_string(),
8685                        module: scope.module.clone(),
8686                        class_unit: Some(class_unit.clone()),
8687                        template_signature: scope.template_signature.clone(),
8688                        template_metadata: None,
8689                        declarations_are_fields: true,
8690                        recovered_specialization_member_scope: false,
8691                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
8692                        visible_using_declarations: scope.visible_using_declarations.clone(),
8693                    };
8694                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8695                        if candidate.start_byte() >= fragmented.reparse_end {
8696                            break;
8697                        }
8698                        if cpp_fragment_sibling_is_class_member(
8699                            candidate,
8700                            fragmented.reparse_end,
8701                            self.source,
8702                        ) {
8703                            self.recovered_class_sibling_scopes
8704                                .insert(candidate.id(), member_scope.clone());
8705                        }
8706                    }
8707                    if let Some(range) = recovered_constructor
8708                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8709                    {
8710                        let prefix_root = prefix_tree.root_node();
8711                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
8712                        self.visit_recovered_fragment_prefix_members(
8713                            node,
8714                            prefix_root,
8715                            range.start,
8716                            &class_unit,
8717                            scope,
8718                            &prefix_ancestry,
8719                        );
8720                        self.visit_recovered_fragment_constructor(
8721                            range,
8722                            body,
8723                            class_node,
8724                            &class_unit,
8725                            scope,
8726                            ancestry,
8727                        );
8728                    }
8729                }
8730                if let Some(boundary) = displaced_namespace {
8731                    for item in boundary.namespace_items {
8732                        self.recovered_class_sibling_scopes
8733                            .insert(item.id(), scope.clone());
8734                    }
8735                }
8736                stack.extend(class_stack);
8737                return;
8738            }
8739            let mut stack = Vec::new();
8740            let class_unit = self.visit_named_class_like_shape(
8741                class_node,
8742                name,
8743                body,
8744                body.is_some(),
8745                None,
8746                raw_supertypes,
8747                base_facts,
8748                scope,
8749                &mut stack,
8750                ancestry,
8751            );
8752            self.parsed
8753                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8754                    recovery: cpp_declaration_range(node),
8755                    unit: class_unit,
8756                });
8757            // Issue #1524: the bogus `function_definition` body can run past
8758            // the class's true closing brace (the parse ends it with a
8759            // zero-width `MISSING "}"`), swallowing following namespace-scope
8760            // siblings -- they would index as members of the recovered class.
8761            // When the body's text-balanced close lands before the body's own
8762            // end, re-own the swallowed tail with the outer scope instead.
8763            if let Some(body) = body
8764                && let Some(class_close) = self
8765                    .orphaned_namespaces
8766                    .matching_close_brace(body.start_byte())
8767                && class_close.start_byte < body.end_byte()
8768            {
8769                let split = {
8770                    let mut cursor = body.walk();
8771                    body.named_children(&mut cursor)
8772                        .position(|child| child.start_byte() > class_close.start_byte)
8773                };
8774                if let Some(split) = split {
8775                    // The seeded work is a single Container over the whole
8776                    // body with the class scope; replace it with the bounded
8777                    // head (class scope) plus the swallowed tail (outer
8778                    // scope). Push tail first so the head drains first.
8779                    let seeded = stack.pop();
8780                    match seeded {
8781                        Some(CppWork::Container(container)) => {
8782                            push_cpp_sibling_range(
8783                                body,
8784                                split,
8785                                usize::MAX,
8786                                scope.clone(),
8787                                &mut stack,
8788                            );
8789                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8790                        }
8791                        // visit_named_class_like_shape always seeds exactly
8792                        // one Container when a body is present.
8793                        _ => unreachable!("exported-class seed is always one Container"),
8794                    }
8795                }
8796            }
8797            while let Some(work) = stack.pop() {
8798                match work {
8799                    CppWork::Container(container) => {
8800                        push_cpp_container_work(container.node, container.scope, &mut stack);
8801                    }
8802                    CppWork::Siblings(siblings) => {
8803                        advance_cpp_siblings(siblings, self.source, &mut stack);
8804                    }
8805                    CppWork::Node(work) => {
8806                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8807                    }
8808                }
8809            }
8810            return;
8811        }
8812        let recovered_constraint_constructor =
8813            cpp_recovered_template_macro_constructor(node, self.source);
8814        // A constraint-macro template head the parser split off keeps the
8815        // function's declared type parameter, so the member stays a template.
8816        let constrained_scope;
8817        let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8818            Some(parameter) => {
8819                constrained_scope = ScopeInfo {
8820                    template_signature: Some(format!(
8821                        "<{}>",
8822                        normalize_cpp_whitespace(node_text(parameter, self.source))
8823                    )),
8824                    ..scope.clone()
8825                };
8826                &constrained_scope
8827            }
8828            None => scope,
8829        };
8830        let declarator = recovered_constraint_constructor
8831            .map(|(declarator, _)| declarator)
8832            .or_else(|| {
8833                scope.class_unit.as_ref().and_then(|class| {
8834                    recovered_macro_decorated_constructor_declarator(
8835                        node,
8836                        class.identifier(),
8837                        self.source,
8838                    )
8839                })
8840            })
8841            .or_else(|| node.child_by_field_name("declarator"));
8842        let Some(declarator) = declarator else {
8843            self.visit_malformed_function_definition_container(node, scope, stack);
8844            return;
8845        };
8846        let Some(function_declarator) = extract_function_declarator(declarator) else {
8847            self.visit_malformed_function_definition_container(node, scope, stack);
8848            return;
8849        };
8850        let function = if let Some((_, callable_name)) =
8851            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8852        {
8853            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8854        } else {
8855            extract_function_info(function_declarator, self.source, scope)
8856        };
8857        let Some(mut function) = function else {
8858            self.visit_malformed_function_definition_container(node, scope, stack);
8859            return;
8860        };
8861        if let Some((_, template_parameter)) = recovered_constraint_constructor {
8862            function.signature = format!(
8863                "template <{}>{}",
8864                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8865                function.signature
8866            );
8867        }
8868        let code_unit = function.code_unit(self.file.clone());
8869        // Keep an earlier same-file prototype as another physical occurrence
8870        // of this callable. `CodeUnit` already identifies the role-neutral
8871        // overload, while ranges and signature metadata describe its
8872        // declaration/definition occurrences.
8873        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8874        let signature = if recovered_constraint_constructor.is_some() {
8875            normalize_cpp_whitespace(node_text(function_declarator, self.source))
8876        } else {
8877            render_cpp_function_display_signature_from_node(
8878                node,
8879                self.source,
8880                scope.template_signature.as_deref(),
8881                true,
8882                ancestry,
8883            )
8884        };
8885        self.add_signature_with_metadata(
8886            code_unit.clone(),
8887            cpp_signature_metadata(
8888                signature,
8889                function_declarator,
8890                cpp_callable_linkage(node, self.source, ancestry),
8891                self.source,
8892                ancestry,
8893            )
8894            .with_declaration_only(false),
8895        );
8896        if let Some(parent) = &scope.class_unit {
8897            self.parsed.add_child(parent.clone(), code_unit);
8898        } else if let Some(module) = &scope.module {
8899            self.parsed.add_child(module.clone(), code_unit);
8900        }
8901        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
8902    }
8903
8904    /// Recover the namespace lost when tree-sitter promotes an export-macro
8905    /// class definition to a root-level `function_definition`.  Only a
8906    /// body-bearing, top-level recovery may borrow a namespace, and only when
8907    /// one earlier namespace-scope forward declaration proves the identity.
8908    fn scope_for_recovered_exported_class<'tree>(
8909        &mut self,
8910        node: Node<'tree>,
8911        name: &str,
8912        definition_body_present: bool,
8913        scope: &ScopeInfo,
8914        ancestry: &ParentIndex<'tree>,
8915    ) -> ScopeInfo {
8916        if !definition_body_present
8917            || !scope.package_name.is_empty()
8918            || scope.class_unit.is_some()
8919            || !(is_recovered_exported_class_container(node, self.source)
8920                || recover_function_like_export_class_pair(node, self.source).is_some()
8921                || recover_embedded_function_like_export_classes(node, self.source)
8922                    .iter()
8923                    .any(|recovered| recovered.name == name)
8924                || matches!(node.kind(), "declaration" | "field_declaration")
8925                    && recover_exported_class_declaration(node, self.source).is_some()
8926                || matches!(
8927                    node.kind(),
8928                    "class_specifier" | "struct_specifier" | "union_specifier"
8929                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
8930                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
8931                        name_node,
8932                        self.source,
8933                    )))
8934                }) || ancestry.parent(node).is_some_and(|parent| {
8935                    matches!(parent.kind(), "declaration" | "field_declaration")
8936                        && recover_exported_class_declaration(parent, self.source).is_some()
8937                        || is_recovered_exported_class_container(parent, self.source)
8938                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
8939        {
8940            return scope.clone();
8941        }
8942        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
8943        let Some(package_name) = borrowed_namespace
8944            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
8945        else {
8946            return scope.clone();
8947        };
8948
8949        let module = CodeUnit::new_fq(
8950            self.file.clone(),
8951            CodeUnitType::Module,
8952            "",
8953            package_name.clone(),
8954            cpp_namespace_fq(&package_name),
8955        );
8956        let mut recovered = scope.clone();
8957        recovered.package_name = package_name;
8958        recovered.module = Some(module);
8959        recovered
8960    }
8961
8962    /// The unique namespace-scope forward declaration of `name` that precedes
8963    /// `recovered_node`, answered from the walk's carried-forward scan of the
8964    /// tree `recovered_node` belongs to.
8965    ///
8966    /// The scan is built on the first question and advanced by each later one,
8967    /// so a file that never reaches this path -- almost every file -- pays
8968    /// nothing, and one that reaches it thousands of times pays a single pass
8969    /// (#2754).
8970    fn unique_earlier_namespace_forward<'tree>(
8971        &mut self,
8972        recovered_node: Node<'tree>,
8973        name: &str,
8974        ancestry: &ParentIndex<'tree>,
8975    ) -> Option<String> {
8976        let mut root = recovered_node;
8977        while let Some(parent) = ancestry.parent(root) {
8978            root = parent;
8979        }
8980        let source = self.source;
8981        let scan = self
8982            .namespace_forward_scans
8983            .entry(CppTreeIdentity::of(root))
8984            .or_default();
8985        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
8986        let borrowed = scan.unique_earlier_forward(name, recovered_node);
8987
8988        #[cfg(debug_assertions)]
8989        assert_eq!(
8990            borrowed,
8991            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
8992            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
8993             for {name} at byte {}",
8994            recovered_node.start_byte()
8995        );
8996
8997        borrowed
8998    }
8999
9000    fn visit_malformed_function_definition_container<'tree>(
9001        &mut self,
9002        node: Node<'tree>,
9003        scope: &ScopeInfo,
9004        stack: &mut Vec<CppWork<'tree>>,
9005    ) {
9006        let Some(body) = cpp_body_node(node) else {
9007            return;
9008        };
9009        if !cpp_contains_namespace_definition(body) {
9010            return;
9011        }
9012        stack.push(CppWork::Container(CppContainer {
9013            node: body,
9014            scope: scope.clone(),
9015        }));
9016    }
9017
9018    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
9019    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
9020    /// emits for a sentinel-prefixed region, reparse the interior after the
9021    /// sentinel identifier as real C++ items -- confined to the region so
9022    /// every reparsed node keeps its original byte/line position -- and run the
9023    /// ordinary container visitation over the result. Returns `true` when it fired
9024    /// (the caller must then skip normal function processing). Nested sentinel
9025    /// regions recover recursively: the reparsed interior is walked through the
9026    /// same `visit_function_definition` path, so a sentinel inside the region hits
9027    /// this recovery again.
9028    /// Runs `reparse_walk` and records every declaration it mints as a
9029    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
9030    /// `recovery` (issue #1657). A reparsed sentinel region has no single
9031    /// recovered envelope unit: the ordinary visitors mint namespaces,
9032    /// classes, and members directly from the reparsed tree, so the walk's
9033    /// declaration delta is the recovered set. Records are ordered by
9034    /// declaration start byte so the parse product stays deterministic.
9035    fn record_recovered_declarations(
9036        &mut self,
9037        recovery: Range,
9038        origin: Node<'_>,
9039        reparse_walk: impl FnOnce(&mut Self),
9040    ) {
9041        // The recovered declarations begin at the recovery start, which can lie
9042        // past a branch directive the malformed origin swallowed (#6325:
9043        // `END_NS #else BEGIN_NS class C {};` parses as one declaration inside
9044        // the `#if` branch). Read their preprocessor context there.
9045        let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9046            && recovery.start_byte < origin.end_byte())
9047        .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9048        .flatten()
9049        .unwrap_or(origin);
9050        self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9051            let source_context = visitor.active_source_context.clone();
9052            visitor.record_recovered_declarations_with_context(
9053                recovery,
9054                source_context,
9055                reparse_walk,
9056            );
9057        });
9058    }
9059
9060    /// Run a recovered walk with an owned source context and record every
9061    /// declaration it mints. Deferred partitioned regions cannot retain their
9062    /// origin `Node`, so they capture the context while the origin tree is
9063    /// live and use this same materialization path when drained.
9064    fn record_recovered_declarations_with_context(
9065        &mut self,
9066        recovery: Range,
9067        source_context: Option<CppActiveSourceContext>,
9068        reparse_walk: impl FnOnce(&mut Self),
9069    ) {
9070        // The set difference this used to be, kept as the oracle every answer
9071        // is asserted against (#2787).
9072        #[cfg(any(debug_assertions, test))]
9073        let before = self.parsed.declarations().clone();
9074
9075        self.recovery_captures.push(CppRecoveryCapture::default());
9076        let previous = self.active_source_context.take();
9077        self.active_source_context = source_context;
9078        reparse_walk(self);
9079        self.active_source_context = previous;
9080        let captured = self
9081            .recovery_captures
9082            .pop()
9083            .expect("the capture this call pushed is the one it pops");
9084
9085        // The capture holds every declaration created while it was open, once
9086        // each and in creation order, so the recovered set costs what the
9087        // recovery made rather than everything the file has declared so far.
9088        // One filter is left to apply: a created declaration that a later
9089        // deferred replacement removed is not in the parse product to report.
9090        let mut minted: Vec<CodeUnit> = captured
9091            .created
9092            .into_iter()
9093            .filter(|unit| self.parsed.contains_declaration(unit))
9094            .collect();
9095        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9096
9097        #[cfg(any(debug_assertions, test))]
9098        {
9099            let mut rediscovered: Vec<CodeUnit> = self
9100                .parsed
9101                .declarations()
9102                .iter()
9103                .filter(|unit| !before.contains(*unit))
9104                .cloned()
9105                .collect();
9106            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9107            assert_eq!(
9108                minted, rediscovered,
9109                "the captured recovered set must be the declaration delta of the reparse \
9110                 walk over {recovery:?}"
9111            );
9112        }
9113
9114        for unit in minted {
9115            self.parsed
9116                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9117                    recovery,
9118                    unit,
9119                });
9120        }
9121    }
9122
9123    /// Where one recovered declaration sorts: by start byte, then by name, so
9124    /// the parse product stays deterministic.
9125    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9126        let start = self
9127            .parsed
9128            .declaration_ranges(unit)
9129            .first()
9130            .map(|range| range.start_byte)
9131            .unwrap_or(usize::MAX);
9132        (start, unit.fq_name().to_string())
9133    }
9134
9135    fn visit_sentinel_macro_region<'tree>(
9136        &mut self,
9137        node: Node<'tree>,
9138        scope: &ScopeInfo,
9139        stack: &mut Vec<CppWork<'tree>>,
9140        ancestry: &ParentIndex<'tree>,
9141    ) -> bool {
9142        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9143            return true;
9144        }
9145        if let Some((
9146            reparse_start,
9147            class_start,
9148            body_start,
9149            class_close_start,
9150            class_close_end,
9151            class_close_line,
9152        )) = cpp_sentinel_macro_class_region(node, self.source)
9153        {
9154            let Some(class_tree) =
9155                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9156            else {
9157                return false;
9158            };
9159            let class_root = class_tree.root_node();
9160            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9161            // A region reparse is its own tree and needs its own parent index.
9162            let class_ancestry = self.recovery_ancestry(class_root);
9163            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9164                class_root,
9165                template_node,
9166                self.source,
9167                &class_ancestry,
9168            ) else {
9169                return false;
9170            };
9171            let class_node = reparsed_class.declaration_node;
9172            let name = reparsed_class.name;
9173            let mut class_scope = scope.clone();
9174            if let Some(template_node) = template_node {
9175                class_scope.template_signature =
9176                    cpp_template_signature(template_node, class_node, self.source);
9177                class_scope.template_metadata =
9178                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9179            }
9180            let Some(body_tree) =
9181                cpp_reparse_region_items(self.source, body_start, class_close_start)
9182            else {
9183                return false;
9184            };
9185            let raw_supertypes = reparsed_class.raw_supertypes;
9186            let base_facts = reparsed_class.base_facts;
9187            let class_range = Range {
9188                start_byte: class_start,
9189                end_byte: class_close_end,
9190                start_line: class_node.start_position().row + 1,
9191                end_line: class_close_line,
9192            };
9193            let class_scope = self.scope_for_recovered_exported_class(
9194                class_node,
9195                &name,
9196                true,
9197                &class_scope,
9198                &class_ancestry,
9199            );
9200            let mut class_stack = Vec::new();
9201            self.with_recovery_source_context(node, |visitor| {
9202                let class_unit = visitor.visit_named_class_like_shape(
9203                    class_node,
9204                    name,
9205                    None,
9206                    true,
9207                    Some(class_range),
9208                    raw_supertypes,
9209                    base_facts,
9210                    &class_scope,
9211                    &mut class_stack,
9212                    &class_ancestry,
9213                );
9214                visitor.parsed.record_materialization(
9215                    MaterializationRecord::RecoveredDeclaration {
9216                        recovery: class_range,
9217                        unit: class_unit.clone(),
9218                    },
9219                );
9220                let member_scope = ScopeInfo {
9221                    package_name: class_scope.package_name.clone(),
9222                    module: class_scope.module.clone(),
9223                    class_unit: Some(class_unit),
9224                    template_signature: class_scope.template_signature.clone(),
9225                    template_metadata: None,
9226                    declarations_are_fields: true,
9227                    recovered_specialization_member_scope: false,
9228                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9229                    visible_using_declarations: class_scope.visible_using_declarations.clone(),
9230                };
9231                // The padded body reparse is its own tree, so it indexes itself.
9232                let body_root = body_tree.root_node();
9233                visitor.run_container_work(
9234                    body_root,
9235                    member_scope,
9236                    &visitor.recovery_ancestry(body_root),
9237                );
9238            });
9239            // Register only after the padded body reparse: its nodes deliberately
9240            // retain offsets inside the consumed region and must be visited first.
9241            self.consumed_fragment_regions
9242                .push((node.start_byte(), class_close_end));
9243            // An ERROR envelope can hold real sibling declarations after the
9244            // recovered class's close (the suffix-reparse boundary in
9245            // `cpp_sentinel_macro_class_region` partitions, it does not
9246            // consume). Walk the envelope's remaining children normally; the
9247            // consumed region above keeps the recovered class from being
9248            // indexed twice.
9249            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9250                stack.push(CppWork::Container(CppContainer {
9251                    node,
9252                    scope: scope.clone(),
9253                }));
9254            }
9255            return true;
9256        }
9257        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9258            return false;
9259        };
9260        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9261            return false;
9262        };
9263        let root = tree.root_node();
9264        if !cpp_reparsed_items_are_indexable(root, self.source) {
9265            return false;
9266        }
9267        let recovery = cpp_recovery_window(self.source, start, end);
9268        // The reparsed region is its own tree, so this walk indexes it itself.
9269        let reparsed_ancestry = self.recovery_ancestry(root);
9270        self.record_recovered_declarations(recovery, node, |visitor| {
9271            visitor.visit_container(
9272                root,
9273                &reparsed_ancestry,
9274                &scope.package_name,
9275                scope.module.clone(),
9276                scope.class_unit.clone(),
9277                scope.template_signature.clone(),
9278                scope.visible_using_namespaces.clone(),
9279                scope.visible_using_declarations.clone(),
9280            );
9281        });
9282        if end > node.end_byte() {
9283            self.consumed_fragment_regions
9284                .push((node.start_byte(), end));
9285        } else if node.kind() == "ERROR" && node.end_byte() > end {
9286            // The sentinel region ended at the first recovered class-like item
9287            // but the ERROR envelope keeps real sibling declarations after it
9288            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
9289            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
9290            // envelope's remaining children normally; the consumed region
9291            // keeps the reparsed prefix from being indexed twice.
9292            self.consumed_fragment_regions
9293                .push((node.start_byte(), end));
9294            stack.push(CppWork::Container(CppContainer {
9295                node,
9296                scope: scope.clone(),
9297            }));
9298        }
9299        true
9300    }
9301
9302    /// Re-own complete class declarations from the structured Abseil
9303    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
9304    /// its direct CST children already prove both namespace components and the
9305    /// class bodies, so the ordinary class/member visitor can retain ownership
9306    /// and exact source ranges without admitting unrelated callable bodies.
9307    fn visit_nested_namespace_sentinel<'tree>(
9308        &mut self,
9309        node: Node<'tree>,
9310        scope: &ScopeInfo,
9311        ancestry: &ParentIndex<'tree>,
9312    ) -> bool {
9313        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9314            return false;
9315        };
9316        let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9317            recovered.function,
9318            &recovered.namespace_components,
9319            self.source,
9320        );
9321
9322        let mut package_name = scope.package_name.clone();
9323        let mut module = scope.module.clone();
9324        for component in recovered.namespace_components {
9325            package_name = if package_name.is_empty() {
9326                component
9327            } else {
9328                format!("{package_name}::{component}")
9329            };
9330            let namespace_module = CodeUnit::new_fq(
9331                self.file.clone(),
9332                CodeUnitType::Module,
9333                "",
9334                package_name.clone(),
9335                cpp_namespace_fq(&package_name),
9336            );
9337            if !self.parsed.contains_declaration(&namespace_module) {
9338                self.add_declaration(
9339                    ancestry,
9340                    namespace_module.clone(),
9341                    recovered.function,
9342                    None,
9343                    None,
9344                );
9345            }
9346            module = Some(namespace_module);
9347        }
9348
9349        let recovered_scope = ScopeInfo {
9350            package_name,
9351            module,
9352            // This scope's package comes from the namespace levels the sentinel
9353            // shape proves, so its owner must come from the same place. C++
9354            // never nests a namespace inside a class, so a surviving
9355            // `class_unit` here is recovery bleed from an earlier malformed
9356            // region, and keeping it would publish declarations whose package
9357            // names one lexical position and whose owner names another -- the
9358            // hybrid identity that trips the package/short boundary assert in
9359            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
9360            // identity-neutral for valid code, where `class_unit` is always
9361            // empty at a namespace.
9362            class_unit: None,
9363            template_signature: scope.template_signature.clone(),
9364            template_metadata: scope.template_metadata.clone(),
9365            declarations_are_fields: false,
9366            recovered_specialization_member_scope: false,
9367            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9368            visible_using_declarations: scope.visible_using_declarations.clone(),
9369        };
9370        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9371            recovered.function,
9372            recovered.body,
9373            self.source,
9374            ancestry,
9375        ) {
9376            let mut class_scope = recovered_scope.clone();
9377            if let Some(template_node) = fragmented.template_node {
9378                class_scope.template_signature =
9379                    cpp_template_signature(template_node, fragmented.class_node, self.source);
9380                class_scope.template_metadata = cpp_template_metadata(
9381                    template_node,
9382                    fragmented.class_node,
9383                    self.source,
9384                    ancestry,
9385                );
9386            }
9387            if let Some(outcome) = self
9388                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9389            {
9390                let mut class_stack = Vec::new();
9391                let class_unit = self.visit_named_class_like_shape(
9392                    fragmented.class_node,
9393                    fragmented.name.clone(),
9394                    None,
9395                    true,
9396                    Some(fragmented.fragmented.class_range),
9397                    fragmented.raw_supertypes.clone(),
9398                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9399                    &class_scope,
9400                    &mut class_stack,
9401                    ancestry,
9402                );
9403                self.parsed
9404                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
9405                        recovery: fragmented.fragmented.class_range,
9406                        unit: class_unit.clone(),
9407                    });
9408                let mut class_lexical_scope = sentinel_namespace.clone();
9409                class_lexical_scope.push(fragmented.name.clone());
9410                self.visit_sentinel_class_aliases(
9411                    cpp_sentinel_fragmented_class_aliases(&fragmented),
9412                    &class_unit,
9413                    &class_scope,
9414                    &class_lexical_scope,
9415                    ancestry,
9416                );
9417                if self.visit_fragmented_export_class_members(
9418                    node,
9419                    outcome,
9420                    class_unit,
9421                    &class_scope,
9422                ) {
9423                    self.consumed_fragment_regions.push((
9424                        fragmented.consumed_start,
9425                        fragmented.fragmented.class_range.end_byte,
9426                    ));
9427                }
9428            }
9429        }
9430        // The class requirement above is the admission gate; once admitted,
9431        // traverse the whole proven inner namespace body so sibling aliases,
9432        // functions, and variables are not silently discarded. The body is a
9433        // node of the tree being walked, so it reuses that tree's index.
9434        self.run_container_work(recovered.body, recovered_scope, ancestry);
9435        true
9436    }
9437
9438    /// Publish the original structured aliases of a recovered sentinel class
9439    /// as its members, then claim their byte ranges. The claim keeps the member
9440    /// reparse from publishing its statement-context misreading of the same
9441    /// text, and keeps the outer walk from publishing the alias again at
9442    /// namespace scope when the reparse leaves the class region unclaimed.
9443    ///
9444    /// The malformed CST ancestry names only the namespaces outside the
9445    /// sentinel, so the target type is read in `lexical_scope`, the recovered
9446    /// class scope the usage extractor also assigns to these nodes.
9447    fn visit_sentinel_class_aliases<'tree>(
9448        &mut self,
9449        aliases: Vec<Node<'tree>>,
9450        class_unit: &CodeUnit,
9451        scope: &ScopeInfo,
9452        lexical_scope: &[String],
9453        ancestry: &ParentIndex<'tree>,
9454    ) {
9455        let member_scope = ScopeInfo {
9456            package_name: class_unit.package_name().to_string(),
9457            module: scope.module.clone(),
9458            class_unit: Some(class_unit.clone()),
9459            template_signature: scope.template_signature.clone(),
9460            template_metadata: None,
9461            declarations_are_fields: true,
9462            recovered_specialization_member_scope: false,
9463            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9464            visible_using_declarations: scope.visible_using_declarations.clone(),
9465        };
9466        for alias in aliases {
9467            let mut alias_scope = member_scope.clone();
9468            let declaration = if alias.kind() == "template_declaration" {
9469                let declaration = alias
9470                    .named_children(&mut alias.walk())
9471                    .find(|child| child.kind() == "alias_declaration")
9472                    .expect("an admitted template alias holds its alias declaration");
9473                alias_scope.template_signature =
9474                    cpp_template_signature(alias, declaration, self.source);
9475                alias_scope.template_metadata =
9476                    cpp_template_metadata(alias, declaration, self.source, ancestry);
9477                declaration
9478            } else {
9479                alias
9480            };
9481            self.consumed_fragment_regions
9482                .push((alias.start_byte(), alias.end_byte()));
9483            let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9484                continue;
9485            };
9486            let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9487            let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9488            self.record_type_aliases(
9489                ancestry,
9490                declaration,
9491                &alias_scope,
9492                vec![name.clone()],
9493                signature,
9494                cpp_declaration_range(declaration),
9495                lexical_scope,
9496            );
9497            // The source fact reads its lexical path from the same malformed
9498            // ancestry; structural alias navigation joins this path.
9499            if let Some(declaration) = self
9500                .source_links
9501                .get(&unit)
9502                .and_then(|declarations| declarations.last())
9503            {
9504                let mut properties = self.source_properties.borrow_mut();
9505                let fact = properties
9506                    .get_mut(declaration)
9507                    .expect("a linked alias has a source fact");
9508                fact.lexical_path = lexical_scope.to_vec();
9509                fact.lexical_path.push(name);
9510            }
9511        }
9512    }
9513
9514    fn visit_declaration<'tree>(
9515        &mut self,
9516        node: Node<'tree>,
9517        scope: &ScopeInfo,
9518        in_class_body: bool,
9519        stack: &mut Vec<CppWork<'tree>>,
9520        ancestry: &ParentIndex<'tree>,
9521    ) {
9522        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9523            return;
9524        }
9525        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9526            return;
9527        }
9528        // Statement-context parsing treats Ctor(int value) as a parenthesized
9529        // variable declarator. Validate it as a class member before publishing
9530        // its parameter name as a field.
9531        if in_class_body
9532            && scope.class_unit.as_ref().is_some_and(|owner| {
9533                node.child_by_field_name("type").is_some_and(|ty| {
9534                    ty.kind() == "type_identifier"
9535                        && node_text(ty, self.source) == owner.identifier()
9536                })
9537            })
9538            && node
9539                .child_by_field_name("declarator")
9540                .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9541            && self.visit_bare_constructor_expression(node, scope, ancestry)
9542        {
9543            return;
9544        }
9545        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9546            !cpp_active_template_type_parameter(
9547                node,
9548                node_text(declarator, self.source),
9549                self.source,
9550                ancestry,
9551            )
9552        }) {
9553            return;
9554        }
9555        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9556            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
9557            // the following `Imaging image` member into one malformed field.
9558            // The ERROR's identifier is the actual declarator; route it
9559            // through the ordinary field path so its parent, range, and
9560            // signature metadata remain consistent with every other member.
9561            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9562            return;
9563        }
9564        if in_class_body
9565            && let Some(parent) = scope.class_unit.as_ref()
9566            && let Some(call) =
9567                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9568        {
9569            self.visit_recovered_macro_qualified_constructor_definition(
9570                node, call, scope, ancestry,
9571            );
9572            return;
9573        }
9574        if in_class_body
9575            && let Some(parent) = scope.class_unit.as_ref()
9576            && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9577                node,
9578                parent.identifier(),
9579                self.source,
9580            )
9581        {
9582            self.visit_recovered_macro_initialized_constructor_definition(
9583                node,
9584                constructor,
9585                scope,
9586                ancestry,
9587            );
9588            return;
9589        }
9590        if in_class_body
9591            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9592        {
9593            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9594            return;
9595        }
9596        if in_class_body
9597            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9598        {
9599            for member in members {
9600                self.add_macro_wrapped_declaration(member, scope, ancestry);
9601            }
9602            return;
9603        }
9604        if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9605            return;
9606        }
9607        if in_class_body
9608            && let Some(declarators) =
9609                recovered_macro_qualified_field_declarators(node, self.source)
9610        {
9611            for declarator in declarators {
9612                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9613            }
9614            return;
9615        }
9616        let recovered_alias_names = recovered_type_alias_names(node, self.source);
9617        if !recovered_alias_names.is_empty() {
9618            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9619            return;
9620        }
9621        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9622            return;
9623        }
9624        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9625            return;
9626        }
9627
9628        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9629            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9630                // Issue #938: the members tree-sitter scattered out of the fragmented
9631                // multiple-base export node are reparsed from their true body region
9632                // and re-owned as members of the recovered class, with an explicit
9633                // navigation range spanning to the displaced closing brace.
9634                if let Some(outcome) =
9635                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9636                {
9637                    let consumed_region = (
9638                        recovered.declaration_node.end_byte(),
9639                        fragmented.class_range.end_byte,
9640                    );
9641                    let code_unit = self.visit_named_class_like_shape(
9642                        recovered.declaration_node,
9643                        recovered.name,
9644                        None,
9645                        true,
9646                        Some(fragmented.class_range),
9647                        recovered.raw_supertypes,
9648                        recovered.base_facts,
9649                        scope,
9650                        stack,
9651                        ancestry,
9652                    );
9653                    self.parsed.record_materialization(
9654                        MaterializationRecord::RecoveredDeclaration {
9655                            recovery: fragmented.class_range,
9656                            unit: code_unit.clone(),
9657                        },
9658                    );
9659                    let consume_fragment =
9660                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9661                    // Everything between the fragmented declaration and its displaced
9662                    // closing brace now belongs to the recovered class; keep the
9663                    // ordinary walk from re-indexing those scattered siblings at top
9664                    // level. Register the consumed region only after indexing because
9665                    // the reparsed nodes retain byte offsets inside that same region.
9666                    if consume_fragment {
9667                        self.consumed_fragment_regions.push(consumed_region);
9668                    }
9669                    return;
9670                }
9671            }
9672            let uses_initializer_body = recovered.uses_initializer_body;
9673            let definition_body_present = recovered.body.is_some();
9674            let class_unit = self.visit_named_class_like_shape(
9675                recovered.declaration_node,
9676                recovered.name,
9677                recovered.body,
9678                definition_body_present,
9679                None,
9680                recovered.raw_supertypes,
9681                recovered.base_facts,
9682                scope,
9683                stack,
9684                ancestry,
9685            );
9686            self.parsed
9687                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9688                    recovery: cpp_declaration_range(node),
9689                    unit: class_unit,
9690                });
9691            if uses_initializer_body {
9692                return;
9693            }
9694        }
9695
9696        // A function-like macro invocation that expands to declarations keeps
9697        // no terminator of its own: the semicolons live in the replacement
9698        // body, which is not part of this tree. Tree-sitter recovers one and
9699        // reads the macro name as the member type and its argument list as a
9700        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
9701        // published a field named `ClosedDetect` beside the real constructor
9702        // and split every reference to the constructor's name between the two
9703        // (#3298). The invented terminator is the whole difference from a real
9704        // member: a member terminates itself, and with a terminator the
9705        // grammar reads `T (name);` as a declaration rather than a field.
9706        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9707            return;
9708        }
9709
9710        let mut handled_function = false;
9711        let mut handled_declarator = false;
9712        let mut cursor = node.walk();
9713        for child in node.named_children(&mut cursor) {
9714            if matches!(
9715                child.kind(),
9716                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9717            ) {
9718                // A named class-like definition remains a declaration even when
9719                // the same statement also declares an object, for example
9720                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
9721                // declaration's type and `kind` as its declarator.  Dropping the
9722                // type here loses both its nested owner and every later lexical
9723                // reference to it.  A body is the structured proof that this is
9724                // a definition rather than an elaborated type use such as
9725                // `class Kind value;`.
9726                if cpp_body_node(child).is_some() {
9727                    self.visit_class_like(child, scope, stack, ancestry);
9728                }
9729                continue;
9730            }
9731        }
9732
9733        let mut cursor = node.walk();
9734        for child in node.children_by_field_name("declarator", &mut cursor) {
9735            if crate::structural::is_recovered_designator_init_declarator(child) {
9736                handled_declarator = true;
9737                continue;
9738            }
9739            if in_class_body
9740                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9741            {
9742                handled_declarator = true;
9743                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9744                continue;
9745            }
9746            if let Some(kind) = classify_declarator(child) {
9747                handled_declarator = true;
9748                match kind {
9749                    DeclaratorKind::Function(function_declarator) => {
9750                        handled_function = true;
9751                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9752                    }
9753                    DeclaratorKind::Variable(variable_declarator) => {
9754                        self.visit_variable_declaration(
9755                            node,
9756                            variable_declarator,
9757                            scope,
9758                            in_class_body,
9759                            ancestry,
9760                        );
9761                    }
9762                }
9763            }
9764        }
9765
9766        if !handled_declarator {
9767            let mut cursor = node.walk();
9768            for child in node.named_children(&mut cursor) {
9769                if crate::structural::is_recovered_designator_init_declarator(child) {
9770                    handled_declarator = true;
9771                    continue;
9772                }
9773                if !is_unfielded_declarator_candidate(child) {
9774                    continue;
9775                }
9776                let Some(kind) = classify_declarator(child) else {
9777                    continue;
9778                };
9779                handled_declarator = true;
9780                match kind {
9781                    DeclaratorKind::Function(function_declarator) => {
9782                        handled_function = true;
9783                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9784                    }
9785                    DeclaratorKind::Variable(variable_declarator) => {
9786                        self.visit_variable_declaration(
9787                            node,
9788                            variable_declarator,
9789                            scope,
9790                            in_class_body,
9791                            ancestry,
9792                        );
9793                    }
9794                }
9795            }
9796        }
9797
9798        if handled_function {
9799            return;
9800        }
9801
9802        if !handled_declarator {
9803            if in_class_body {
9804                self.visit_class_members_from_declaration(node, scope, ancestry);
9805            } else {
9806                self.visit_global_variables_from_declaration(node, scope, ancestry);
9807            }
9808        }
9809    }
9810
9811    /// Preserve the member structure of an anonymous aggregate.
9812    ///
9813    /// An anonymous union with no declarator promotes its fields into the
9814    /// containing aggregate. An anonymous struct/union followed by a named
9815    /// declarator, such as `struct { T *ops; } sock`, declares both the field
9816    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
9817    /// the declarator's structured nested identity so a later `value.sock.ops`
9818    /// chain can traverse it without parsing a type spelling (#2407).
9819    ///
9820    /// An anonymous aggregate declares no tag, so there is nothing for the C
9821    /// dialect to re-scope and nothing for the C++ dialect to nest: both
9822    /// readings of this shape are the same, and both need these members
9823    /// (#3301).
9824    fn anonymous_aggregate_declaration_parts<'tree>(
9825        node: Node<'tree>,
9826    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9827        let aggregate = node.child_by_field_name("type")?;
9828        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9829            || aggregate.child_by_field_name("name").is_some()
9830        {
9831            return None;
9832        }
9833        let body = cpp_body_node(aggregate)?;
9834        let mut cursor = node.walk();
9835        let declarators = node
9836            .children_by_field_name("declarator", &mut cursor)
9837            .filter_map(|declarator| match classify_declarator(declarator) {
9838                Some(DeclaratorKind::Variable(variable)) => Some(variable),
9839                Some(DeclaratorKind::Function(_)) | None => None,
9840            })
9841            .collect();
9842        Some((aggregate, body, declarators))
9843    }
9844
9845    fn visit_anonymous_aggregate_declaration<'tree>(
9846        &mut self,
9847        node: Node<'tree>,
9848        scope: &ScopeInfo,
9849        in_class_body: bool,
9850        stack: &mut Vec<CppWork<'tree>>,
9851        ancestry: &ParentIndex<'tree>,
9852    ) -> bool {
9853        if !in_class_body || scope.class_unit.is_none() {
9854            return false;
9855        }
9856        let Some((aggregate, body, declarators)) =
9857            Self::anonymous_aggregate_declaration_parts(node)
9858        else {
9859            return false;
9860        };
9861        if declarators.is_empty() {
9862            stack.push(CppWork::Container(CppContainer {
9863                node: body,
9864                scope: scope.clone(),
9865            }));
9866            return true;
9867        }
9868
9869        for declarator in declarators {
9870            let Some(name) = extract_variable_name(declarator, self.source) else {
9871                continue;
9872            };
9873            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9874            // The receiver type's identity is the declarator's name, so its
9875            // range is the field declaration that carries that name token.
9876            // The inner specifier ends at the closing brace, which leaves the
9877            // name the unit is known by outside its own text (#3287).
9878            self.visit_named_class_like_shape(
9879                aggregate,
9880                name,
9881                Some(body),
9882                true,
9883                Some(cpp_declaration_range(node)),
9884                None,
9885                None,
9886                scope,
9887                stack,
9888                ancestry,
9889            );
9890        }
9891        true
9892    }
9893
9894    /// Give a function-local anonymous aggregate a structured owner so its
9895    /// direct pointer bindings can be typed by the usage graph.  Unlike an
9896    /// anonymous aggregate in a class body, there is no source-level tag or
9897    /// typedef to supply an identity.  The aggregate's CST range is therefore
9898    /// part of a generated, collision-resistant identity; no source spelling
9899    /// is consulted.  The declaration's own variable remains a file-level
9900    /// field projection, while the aggregate body is visited as the generated
9901    /// class's field list.
9902    fn visit_anonymous_local_aggregate_declaration<'tree>(
9903        &mut self,
9904        node: Node<'tree>,
9905        scope: &ScopeInfo,
9906        stack: &mut Vec<CppWork<'tree>>,
9907        ancestry: &ParentIndex<'tree>,
9908    ) -> bool {
9909        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
9910            return false;
9911        }
9912        let Some((aggregate, body, declarators)) =
9913            Self::anonymous_aggregate_declaration_parts(node)
9914        else {
9915            return false;
9916        };
9917        if declarators.is_empty() {
9918            return false;
9919        }
9920
9921        for declarator in &declarators {
9922            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
9923        }
9924        let name = format!("<anonymous:{}>", aggregate.start_byte());
9925        self.visit_named_class_like_shape(
9926            aggregate,
9927            name,
9928            Some(body),
9929            true,
9930            None,
9931            None,
9932            None,
9933            scope,
9934            stack,
9935            ancestry,
9936        );
9937        true
9938    }
9939
9940    fn visit_function_declaration<'tree>(
9941        &mut self,
9942        declaration_node: Node<'tree>,
9943        declarator: Node<'tree>,
9944        scope: &ScopeInfo,
9945        ancestry: &ParentIndex<'tree>,
9946    ) {
9947        let Some(function) = extract_function_info(declarator, self.source, scope) else {
9948            return;
9949        };
9950        // An in-class member prototype is a real source declaration, not a
9951        // generated or recovery-only unit, so it keeps the ordinary code-unit
9952        // identity (#3508). #3493 recorded its callable modifiers, which is
9953        // what a canonical procedure key needs; a synthetic unit would still
9954        // refuse that key on identity grounds alone.
9955        let code_unit = function.code_unit(self.file.clone());
9956        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
9957            return;
9958        }
9959        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9960        let signature = render_cpp_function_display_signature_from_node(
9961            declaration_node,
9962            self.source,
9963            scope.template_signature.as_deref(),
9964            false,
9965            ancestry,
9966        );
9967        self.add_signature_with_metadata(
9968            code_unit.clone(),
9969            cpp_signature_metadata(
9970                signature,
9971                declarator,
9972                cpp_callable_linkage(declaration_node, self.source, ancestry),
9973                self.source,
9974                ancestry,
9975            )
9976            .with_declaration_only(true),
9977        );
9978        if let Some(parent) = &scope.class_unit {
9979            self.parsed.add_child(parent.clone(), code_unit);
9980        } else if let Some(module) = &scope.module {
9981            self.parsed.add_child(module.clone(), code_unit);
9982        }
9983    }
9984
9985    fn link_existing_source_declaration(
9986        &mut self,
9987        ancestry: &ParentIndex<'_>,
9988        code_unit: &CodeUnit,
9989        declaration_node: Node<'_>,
9990    ) -> bool {
9991        if !self.parsed.contains_declaration(code_unit) {
9992            return false;
9993        }
9994        self.link_source_declaration(ancestry, code_unit, declaration_node);
9995        self.parsed
9996            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
9997        true
9998    }
9999
10000    fn visit_recovered_macro_qualified_function_declaration<'tree>(
10001        &mut self,
10002        declaration_node: Node<'tree>,
10003        call: Node<'tree>,
10004        scope: &ScopeInfo,
10005        ancestry: &ParentIndex<'tree>,
10006    ) {
10007        let Some(parent) = &scope.class_unit else {
10008            return;
10009        };
10010        let Some(name_node) = call.child_by_field_name("function") else {
10011            return;
10012        };
10013        let Some(arguments) = call.child_by_field_name("arguments") else {
10014            return;
10015        };
10016        let Some((signature, parameter_labels)) =
10017            recovered_macro_qualified_function_parameters(arguments, self.source)
10018        else {
10019            return;
10020        };
10021        let arity = parameter_labels.len();
10022        let function = FunctionInfo {
10023            package_name: scope.package_name.clone(),
10024            owner: Some(CppMemberOwner::Unit(parent.clone())),
10025            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10026            signature,
10027        };
10028        if function.name.is_empty() {
10029            return;
10030        }
10031        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10032        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10033            return;
10034        }
10035        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10036        let signature_label = render_cpp_function_display_signature_from_node(
10037            declaration_node,
10038            self.source,
10039            scope.template_signature.as_deref(),
10040            false,
10041            ancestry,
10042        );
10043        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10044        // Only the callable's parameters came from the reparse: the
10045        // `field_declaration` this recovery reads is a real node in a real
10046        // class body, so its storage class and the body's access ladder state
10047        // this member's modifiers exactly as an ordinary member's do.
10048        let modifiers =
10049            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10050        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10051            .with_declaration_only(true)
10052            .with_callable_arity(CallableArity::exact(arity))
10053            .with_callable_linkage(linkage)
10054            .with_callable_modifiers(
10055                modifiers.is_static,
10056                modifiers.is_constructor,
10057                modifiers.visibility,
10058            );
10059        self.add_signature_with_metadata(code_unit.clone(), metadata);
10060        self.parsed.add_child(parent.clone(), code_unit);
10061    }
10062
10063    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10064        &mut self,
10065        declaration_node: Node<'tree>,
10066        call: Node<'tree>,
10067        scope: &ScopeInfo,
10068        ancestry: &ParentIndex<'tree>,
10069    ) {
10070        let Some(parent) = &scope.class_unit else {
10071            return;
10072        };
10073        let Some(arguments) = call.child_by_field_name("arguments") else {
10074            return;
10075        };
10076        let Some((mut signature, parameter_labels)) =
10077            recovered_macro_qualified_function_parameters(arguments, self.source)
10078        else {
10079            return;
10080        };
10081        if let Some(template_signature) = &scope.template_signature {
10082            signature = format!("{template_signature}{signature}");
10083        }
10084        let arity = parameter_labels.len();
10085        let function = FunctionInfo {
10086            package_name: scope.package_name.clone(),
10087            owner: Some(CppMemberOwner::Unit(parent.clone())),
10088            name: parent.identifier().to_string(),
10089            signature,
10090        };
10091        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10092        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10093        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10094        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10095        // The recovery already proved this member is the class's constructor:
10096        // the reparsed call names the enclosing class. Everything else is read
10097        // from the real `field_declaration` the recovery found it in.
10098        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10099        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10100            .with_declaration_only(false)
10101            .with_callable_arity(CallableArity::exact(arity))
10102            .with_callable_linkage(linkage)
10103            .with_callable_modifiers(
10104                modifiers.is_static,
10105                modifiers.is_constructor,
10106                modifiers.visibility,
10107            );
10108        self.add_signature_with_metadata(code_unit.clone(), metadata);
10109        self.parsed.add_child(parent.clone(), code_unit);
10110    }
10111
10112    /// Publish the constructor of the macro-decorated field shape that
10113    /// `recovered_macro_initialized_constructor_declarator` proves. The head
10114    /// is a real, clean function declarator, so it keeps the ordinary
10115    /// identity and metadata; the `default_value` initializer list is the
10116    /// constructor body, so the occurrence is a definition.
10117    fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10118        &mut self,
10119        declaration_node: Node<'tree>,
10120        function_declarator: Node<'tree>,
10121        scope: &ScopeInfo,
10122        ancestry: &ParentIndex<'tree>,
10123    ) {
10124        let Some(parent) = &scope.class_unit else {
10125            return;
10126        };
10127        let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10128            return;
10129        };
10130        let code_unit = function.code_unit(self.file.clone());
10131        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10132        // Render the head the way an ordinary constructor definition renders
10133        // the text ahead of its body, member initializers included.
10134        let body_start = declaration_node
10135            .child_by_field_name("default_value")
10136            .map_or(declaration_node.end_byte(), |body| body.start_byte());
10137        let head =
10138            collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10139        let signature = match scope.template_signature.as_deref() {
10140            Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10141            None => format!("{head} {{...}}"),
10142        };
10143        self.add_signature_with_metadata(
10144            code_unit.clone(),
10145            cpp_signature_metadata(
10146                signature,
10147                function_declarator,
10148                cpp_callable_linkage(declaration_node, self.source, ancestry),
10149                self.source,
10150                ancestry,
10151            )
10152            .with_declaration_only(false),
10153        );
10154        self.parsed.add_child(parent.clone(), code_unit);
10155    }
10156
10157    fn visit_variable_declaration<'tree>(
10158        &mut self,
10159        declaration_node: Node<'tree>,
10160        declarator: Node<'tree>,
10161        scope: &ScopeInfo,
10162        in_class_body: bool,
10163        ancestry: &ParentIndex<'tree>,
10164    ) {
10165        let Some(name) = extract_variable_name(declarator, self.source) else {
10166            return;
10167        };
10168        let parent = if in_class_body {
10169            let Some(parent) = &scope.class_unit else {
10170                return;
10171            };
10172            Some(parent)
10173        } else {
10174            None
10175        };
10176        let short_name = match parent {
10177            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10178            None => name.clone(),
10179        };
10180        let fq = cpp_leaf_fq(
10181            &scope.package_name,
10182            parent,
10183            &name,
10184            SegmentKind::Member,
10185            SegmentKind::Member,
10186        );
10187        let code_unit = CodeUnit::new_fq(
10188            self.file.clone(),
10189            CodeUnitType::Field,
10190            scope.package_name.clone(),
10191            short_name,
10192            fq,
10193        );
10194        if self.parsed.contains_declaration(&code_unit) {
10195            return;
10196        }
10197        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10198        self.add_signature_with_metadata(
10199            code_unit.clone(),
10200            SignatureMetadata::new(
10201                render_cpp_field_signature(declaration_node, declarator, self.source),
10202                Vec::new(),
10203            )
10204            .with_cpp_field_linkage(cpp_field_declaration_linkage(
10205                declaration_node,
10206                self.source,
10207                ancestry,
10208            )),
10209        );
10210        if let Some(parent) = &scope.class_unit {
10211            self.parsed.add_child(parent.clone(), code_unit);
10212        } else if let Some(module) = &scope.module {
10213            self.parsed.add_child(module.clone(), code_unit);
10214        }
10215    }
10216
10217    /// Index the field half of an omitted-semicolon pair the macro-declaration
10218    /// grammar folded into the following function definition's head. The
10219    /// function itself continues through the ordinary visitor path.
10220    fn visit_omitted_semicolon_macro_field<'tree>(
10221        &mut self,
10222        node: Node<'tree>,
10223        type_modifier: Node<'tree>,
10224        name_modifier: Node<'tree>,
10225        scope: &ScopeInfo,
10226        ancestry: &ParentIndex<'tree>,
10227    ) {
10228        let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10229            return;
10230        };
10231        let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10232        if name.is_empty() {
10233            return;
10234        }
10235        let range = Range {
10236            start_byte: type_modifier.start_byte(),
10237            end_byte: name_modifier.end_byte(),
10238            start_line: type_modifier.start_position().row + 1,
10239            end_line: name_modifier.end_position().row + 1,
10240        };
10241        self.record_macro_head_field(node, range, name, scope, ancestry);
10242    }
10243
10244    /// Index the macro-return stand-in field: a clean function definition whose
10245    /// macro-modified head returns an active template type parameter. The field
10246    /// is named after the return type and spans the head up to it, exactly the
10247    /// phantom `field_declaration` the older grammar emitted (#1129).
10248    fn visit_macro_return_template_parameter_field<'tree>(
10249        &mut self,
10250        node: Node<'tree>,
10251        head_start: Node<'tree>,
10252        type_node: Node<'tree>,
10253        scope: &ScopeInfo,
10254        ancestry: &ParentIndex<'tree>,
10255    ) {
10256        let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10257        let range = Range {
10258            start_byte: head_start.start_byte(),
10259            end_byte: type_node.end_byte(),
10260            start_line: head_start.start_position().row + 1,
10261            end_line: type_node.end_position().row + 1,
10262        };
10263        self.record_macro_head_field(node, range, name, scope, ancestry);
10264    }
10265
10266    /// Record one field recovered from a function definition's macro head. The
10267    /// signature is the head's own text: like the older grammar's phantom
10268    /// field_declaration, it carries no semicolon because none was written.
10269    fn record_macro_head_field<'tree>(
10270        &mut self,
10271        node: Node<'tree>,
10272        range: Range,
10273        name: String,
10274        scope: &ScopeInfo,
10275        ancestry: &ParentIndex<'tree>,
10276    ) {
10277        if name.is_empty() {
10278            return;
10279        }
10280        let parent = scope.class_unit.as_ref();
10281        let short_name = match parent {
10282            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10283            None => name.clone(),
10284        };
10285        let code_unit = CodeUnit::new_fq(
10286            self.file.clone(),
10287            CodeUnitType::Field,
10288            scope.package_name.clone(),
10289            short_name,
10290            cpp_leaf_fq(
10291                &scope.package_name,
10292                parent,
10293                &name,
10294                SegmentKind::Member,
10295                SegmentKind::Member,
10296            ),
10297        );
10298        if self.parsed.contains_declaration(&code_unit) {
10299            return;
10300        }
10301        self.add_declaration_with_range(
10302            ancestry,
10303            code_unit.clone(),
10304            range,
10305            Some(node),
10306            parent.cloned(),
10307            None,
10308        );
10309        let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10310        self.add_signature_with_metadata(
10311            code_unit.clone(),
10312            SignatureMetadata::new(signature, Vec::new())
10313                .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10314        );
10315        if let Some(parent) = &scope.class_unit {
10316            self.parsed.add_child(parent.clone(), code_unit);
10317        } else if let Some(module) = &scope.module {
10318            self.parsed.add_child(module.clone(), code_unit);
10319        }
10320    }
10321
10322    fn variable_declarator_from_declaration_child<'tree>(
10323        child: Node<'tree>,
10324    ) -> Option<Node<'tree>> {
10325        if child.kind() == "init_declarator" {
10326            child.child_by_field_name("declarator")
10327        } else if matches!(
10328            child.kind(),
10329            "identifier"
10330                | "field_identifier"
10331                | "pointer_declarator"
10332                | "reference_declarator"
10333                | "array_declarator"
10334                | "parenthesized_declarator"
10335                | "macro_modifier_declarator"
10336        ) {
10337            Some(child)
10338        } else {
10339            None
10340        }
10341    }
10342
10343    fn visit_class_members_from_declaration<'tree>(
10344        &mut self,
10345        node: Node<'tree>,
10346        scope: &ScopeInfo,
10347        ancestry: &ParentIndex<'tree>,
10348    ) {
10349        let mut cursor = node.walk();
10350        for child in node.named_children(&mut cursor) {
10351            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10352                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10353            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10354            {
10355                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10356            }
10357        }
10358    }
10359
10360    fn visit_global_variables_from_declaration<'tree>(
10361        &mut self,
10362        node: Node<'tree>,
10363        scope: &ScopeInfo,
10364        ancestry: &ParentIndex<'tree>,
10365    ) {
10366        let mut cursor = node.walk();
10367        for child in node.named_children(&mut cursor) {
10368            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10369                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10370            }
10371        }
10372    }
10373
10374    fn visit_type_declaration<'tree>(
10375        &mut self,
10376        node: Node<'tree>,
10377        scope: &ScopeInfo,
10378        stack: &mut Vec<CppWork<'tree>>,
10379        ancestry: &ParentIndex<'tree>,
10380    ) {
10381        let type_node = node.child_by_field_name("type");
10382        if let Some(type_node) = type_node
10383            && matches!(
10384                type_node.kind(),
10385                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10386            )
10387        {
10388            self.visit_class_like(type_node, scope, stack, ancestry);
10389        }
10390
10391        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10392            let range = Range {
10393                start_byte: node.start_byte(),
10394                end_byte: recovered.end_node.end_byte(),
10395                start_line: node.start_position().row + 1,
10396                end_line: recovered.end_node.end_position().row + 1,
10397            };
10398            let signature = self
10399                .source
10400                .get(range.start_byte..range.end_byte)
10401                .map(normalize_cpp_whitespace)
10402                .unwrap_or_default();
10403            self.record_type_aliases(
10404                ancestry,
10405                node,
10406                scope,
10407                vec![recovered.name],
10408                signature,
10409                range,
10410                &cpp_callable_lexical_scope(node, self.source, ancestry),
10411            );
10412            return;
10413        }
10414
10415        let alias_names = match node.kind() {
10416            "alias_declaration" => extract_alias_declaration_name(node, self.source)
10417                .into_iter()
10418                .collect::<Vec<_>>(),
10419            "type_definition" => extract_typedef_alias_names(node, self.source),
10420            _ => Vec::new(),
10421        };
10422        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10423            (type_node, alias_names.as_slice())
10424            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10425            && type_node.child_by_field_name("name").is_none()
10426        {
10427            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10428        } else {
10429            None
10430        };
10431        self.add_type_aliases(ancestry, node, scope, alias_names);
10432        if let Some((body, alias_name)) = anonymous_aggregate {
10433            // The typedef alias is also the only user-visible identity of an
10434            // anonymous aggregate. Reuse it as the member owner instead of
10435            // minting a second signatureless class with the same FQN. The
10436            // latter makes forward lookup ambiguous when conditional aliases
10437            // coexist and returns duplicate definitions even without guards.
10438            let signature = normalize_cpp_whitespace(node_text(node, self.source));
10439            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10440            debug_assert!(self.parsed.contains_declaration(&alias_unit));
10441            let mut nested_scope = scope.clone();
10442            nested_scope.class_unit = Some(alias_unit);
10443            nested_scope.template_signature = scope.template_signature.clone();
10444            nested_scope.template_metadata = None;
10445            nested_scope.declarations_are_fields = false;
10446            nested_scope.recovered_specialization_member_scope = false;
10447            stack.push(CppWork::Container(CppContainer {
10448                node: body,
10449                scope: nested_scope,
10450            }));
10451        }
10452    }
10453
10454    fn add_type_aliases(
10455        &mut self,
10456        ancestry: &ParentIndex<'_>,
10457        node: Node<'_>,
10458        scope: &ScopeInfo,
10459        alias_names: Vec<String>,
10460    ) {
10461        let signature = normalize_cpp_whitespace(node_text(node, self.source));
10462        self.record_type_aliases(
10463            ancestry,
10464            node,
10465            scope,
10466            alias_names,
10467            signature,
10468            cpp_declaration_range(node),
10469            &cpp_callable_lexical_scope(node, self.source, ancestry),
10470        );
10471    }
10472
10473    /// `lexical_scope` is where the alias target's names are looked up: the
10474    /// alias's CST ancestry, or the owner a structural recovery proved when
10475    /// the CST ancestry is malformed.
10476    #[allow(clippy::too_many_arguments)]
10477    fn record_type_aliases(
10478        &mut self,
10479        ancestry: &ParentIndex<'_>,
10480        node: Node<'_>,
10481        scope: &ScopeInfo,
10482        alias_names: Vec<String>,
10483        signature: String,
10484        range: Range,
10485        lexical_scope: &[String],
10486    ) {
10487        if signature.is_empty() {
10488            return;
10489        }
10490        let type_name = node
10491            .child_by_field_name("type")
10492            .and_then(|type_node| type_node.child_by_field_name("name"))
10493            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10494        for alias_name in alias_names {
10495            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10496                continue;
10497            }
10498            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10499            // Declaration identity does not include the alias signature. Keep
10500            // each physical range so conditional aliases retain their guards.
10501            self.note_declaration(&code_unit);
10502            self.link_source_declaration(ancestry, &code_unit, node);
10503            self.parsed
10504                .add_code_unit_with_range(code_unit.clone(), range, None, None);
10505            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10506                cpp_structured_type_identity(type_node, self.source, lexical_scope)
10507            });
10508            self.add_signature_with_metadata(
10509                code_unit.clone(),
10510                SignatureMetadata::new(signature.clone(), Vec::new())
10511                    .with_underlying_type_identity(underlying_type_identity),
10512            );
10513            if let Some(metadata) = &scope.template_metadata {
10514                let mut metadata = metadata.clone();
10515                metadata.primary_fq_name = code_unit.fq_name();
10516                self.parsed
10517                    .set_cpp_template_metadata(code_unit.clone(), metadata);
10518            }
10519            if let Some(parent) = &scope.class_unit {
10520                self.parsed.add_child(parent.clone(), code_unit.clone());
10521            } else if let Some(module) = &scope.module {
10522                self.parsed.add_child(module.clone(), code_unit.clone());
10523            }
10524            self.parsed.mark_type_alias(code_unit);
10525        }
10526    }
10527
10528    fn type_alias_unit(
10529        &self,
10530        scope: &ScopeInfo,
10531        alias_name: String,
10532        signature: String,
10533    ) -> CodeUnit {
10534        let short_name = if let Some(parent) = &scope.class_unit {
10535            cpp_join_nested_short(parent.short_name(), &alias_name)
10536        } else {
10537            alias_name.clone()
10538        };
10539        let fq = cpp_leaf_fq(
10540            &scope.package_name,
10541            scope.class_unit.as_ref(),
10542            &alias_name,
10543            SegmentKind::Nested,
10544            SegmentKind::Type,
10545        );
10546        CodeUnit::with_signature_and_fq(
10547            self.file.clone(),
10548            CodeUnitType::Class,
10549            scope.package_name.clone(),
10550            short_name,
10551            Some(signature),
10552            false,
10553            fq,
10554        )
10555    }
10556
10557    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10558        // A comment can terminate tree-sitter's preproc_arg before the logical
10559        // directive ends. Its remaining declarations are replacement locals,
10560        // not file-scope fields, even when recovery exposes them as siblings.
10561        if let Some(replacement) =
10562            crate::graph::syntax::function_macro_replacement_span(node, self.source)
10563        {
10564            self.consumed_fragment_regions
10565                .push((replacement.start, replacement.end));
10566        }
10567        let Some(name) = extract_macro_name(node, self.source) else {
10568            return;
10569        };
10570        let signature = node_text(node, self.source).trim_end().to_string();
10571        if signature.is_empty() {
10572            return;
10573        }
10574        let fq = cpp_member_fq("", &name);
10575        // A macro can be undefined and redefined later in the same file. Its
10576        // structured directive is part of the declaration identity so the
10577        // temporal environment can navigate to the definition active at a
10578        // reference instead of collapsing every spelling to the first range.
10579        // The same physical directive parsed through another C/C++ reading
10580        // still produces the same unit and remains deduplicated.
10581        let code_unit = CodeUnit::with_signature_and_fq(
10582            self.file.clone(),
10583            CodeUnitType::Macro,
10584            "",
10585            name.clone(),
10586            Some(signature.clone()),
10587            false,
10588            fq,
10589        );
10590        if !self.parsed.contains_declaration(&code_unit) {
10591            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10592            let name_range = node
10593                .child_by_field_name("name")
10594                .map(cpp_declaration_range)
10595                .unwrap_or_else(|| cpp_declaration_range(node));
10596            self.parsed
10597                .record_materialization(MaterializationRecord::GeneratedDeclaration {
10598                    site: cpp_declaration_range(node),
10599                    argument: name_range,
10600                    kind: GenerationKind::PreprocessorDefinition,
10601                    unit: code_unit.clone(),
10602                });
10603            self.parsed.add_signature(code_unit, signature);
10604        }
10605        if node.kind() == "preproc_def" {
10606            update_object_macro_field_environment(
10607                node,
10608                self.source,
10609                &mut self.object_macro_fields,
10610                &mut self.ambiguous_object_macro_fields,
10611            );
10612        } else {
10613            self.object_macro_fields.remove(&name);
10614            self.ambiguous_object_macro_fields.remove(&name);
10615        }
10616    }
10617
10618    /// Bare object-like field-list macros inside an otherwise well-formed
10619    /// aggregate remain identifier nodes in a field declaration. More than
10620    /// one adjacent invocation can be folded into the same declaration, so
10621    /// inspect all of its structured identifier nodes.
10622    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10623        if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10624            return false;
10625        }
10626        // Claiming the declaration means its members are accounted for, which
10627        // is only true when there is an owner to materialize them into. A file
10628        // scope reaches this with a declaration that merely *contains* an
10629        // aggregate spelling an invocation (tree-sitter wraps libuv's
10630        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
10631        // `field_declaration` under a malformed header), and consuming it there
10632        // would discard the aggregate itself (#2985).
10633        if scope.class_unit.is_none() {
10634            return false;
10635        }
10636        let macro_nodes =
10637            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10638        for macro_node in &macro_nodes {
10639            let name = node_text(*macro_node, self.source).trim();
10640            let fields = object_macro_field_closure(&self.object_macro_fields, name);
10641            self.materialize_object_macro_fields(
10642                fields,
10643                cpp_declaration_range(*macro_node),
10644                *macro_node,
10645                scope,
10646            );
10647        }
10648        let Some(last) = macro_nodes.last() else {
10649            return false;
10650        };
10651        // Claiming the declaration accounts for every member it spells, not
10652        // only the donated ones: an invocation folded together with the
10653        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
10654        // leaves those members shapeless, and the grammar recovers them from
10655        // the byte range that follows the invocation.
10656        self.record_collapsed_aggregate_fields(
10657            last.end_byte()..node.end_byte(),
10658            node.start_position().row
10659                + 1
10660                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10661            node,
10662            scope,
10663        );
10664        true
10665    }
10666
10667    /// Retain the grammar's field interpretation with the original invocation
10668    /// or aggregate context. The replacement AST owns the type, while only the
10669    /// original source owns activation, guards, and occurrence ranges.
10670    fn capture_recovered_field_facts(
10671        &mut self,
10672        unit: &CodeUnit,
10673        range: Range,
10674        context: Node<'_>,
10675        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10676        names_function_type: bool,
10677    ) {
10678        let declaration = *self.source_links[unit]
10679            .last()
10680            .expect("recovered field follows its source occurrence");
10681        let mut fact = crate::source_facts::capture_declaration(
10682            declaration,
10683            context,
10684            unit,
10685            self.source,
10686            self.primary_ancestry,
10687        );
10688        fact.field_type = field_type;
10689        fact.names_function_type = names_function_type;
10690        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10691        let captured = crate::source_context::capture_declaration_context(
10692            context,
10693            unit,
10694            self.source,
10695            &mut self.preprocessor_context_cache,
10696        );
10697        fact.guard_requirements = captured.guard_requirements;
10698        fact.callable_guards = fact.guard_requirements.clone();
10699        fact.callable_activation = Some(range.end_byte);
10700        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10701        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10702        let intern = |node: Node<'_>| {
10703            let mut collector = self.source_collector.borrow_mut();
10704            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10705                collector.intern_node(node)
10706            } else {
10707                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10708            }
10709        };
10710        fact.conditional_family =
10711            crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10712        fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10713        fact.displaced_namespace_closing_brace =
10714            captured.displaced_namespace_closing_brace.map(intern);
10715        self.capture_source_dependencies(&mut fact, context, unit);
10716        self.apply_active_source_context(&mut fact);
10717        assert!(
10718            self.source_properties
10719                .borrow_mut()
10720                .insert(declaration, fact)
10721                .is_none()
10722        );
10723    }
10724
10725    fn materialize_object_macro_fields(
10726        &mut self,
10727        fields: Vec<MacroReplacementField>,
10728        range: Range,
10729        context: Node<'_>,
10730        scope: &ScopeInfo,
10731    ) {
10732        let Some(owner) = scope.class_unit.as_ref() else {
10733            return;
10734        };
10735        for field in fields {
10736            let signature = field.declaration.clone();
10737            let mut fq = owner.fq().clone();
10738            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10739            let short_name = if owner.short_name().is_empty() {
10740                field.name.clone()
10741            } else {
10742                format!("{}.{}", owner.short_name(), field.name)
10743            };
10744            let code_unit = CodeUnit::with_signature_and_fq(
10745                self.file.clone(),
10746                CodeUnitType::Field,
10747                owner.package_name().to_string(),
10748                short_name,
10749                Some(field.declaration),
10750                true,
10751                fq,
10752            );
10753            if self.parsed.contains_declaration(&code_unit) {
10754                continue;
10755            }
10756            self.add_declaration_with_range(
10757                self.primary_ancestry,
10758                code_unit.clone(),
10759                range,
10760                None,
10761                Some(owner.clone()),
10762                None,
10763            );
10764            self.capture_recovered_field_facts(
10765                &code_unit,
10766                range,
10767                context,
10768                field.field_type,
10769                field.names_function_type,
10770            );
10771            self.parsed.add_signature(code_unit, signature);
10772        }
10773    }
10774
10775    fn collapsed_aggregate_owner(
10776        &self,
10777        name: &str,
10778        scope: &ScopeInfo,
10779        parent: Option<&CodeUnit>,
10780    ) -> CodeUnit {
10781        let short_name = parent.map_or_else(
10782            || name.to_string(),
10783            |parent| cpp_join_nested_short(parent.short_name(), name),
10784        );
10785        let fq = cpp_leaf_fq(
10786            &scope.package_name,
10787            parent,
10788            name,
10789            SegmentKind::Nested,
10790            SegmentKind::Type,
10791        );
10792        CodeUnit::with_signature_and_fq(
10793            self.file.clone(),
10794            CodeUnitType::Class,
10795            scope.package_name.clone(),
10796            short_name,
10797            None,
10798            false,
10799            fq,
10800        )
10801    }
10802
10803    /// Recover ordinary aggregates whose field-list macro invocations made
10804    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
10805    /// The class markers, aggregate braces, field nodes, and macro identifier
10806    /// nodes are all read from the AST; no source delimiter or token scan is
10807    /// used. A close-brace node inside a malformed field closes the innermost
10808    /// recovered aggregate, preserving nested-owner identity.
10809    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10810        let mut cursor = node.walk();
10811        let children = node.children(&mut cursor).collect::<Vec<_>>();
10812        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10813        let mut object_macro_fields = self.object_macro_fields.clone();
10814        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10815        let mut open = Vec::<usize>::new();
10816        let mut index = 0;
10817        while index < children.len() {
10818            let keyword = children[index];
10819            if update_object_macro_field_environment(
10820                keyword,
10821                self.source,
10822                &mut object_macro_fields,
10823                &mut ambiguous_object_macro_fields,
10824            ) {
10825                index += 1;
10826                continue;
10827            }
10828            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10829                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10830                if !name.is_empty() {
10831                    let parent = open
10832                        .last()
10833                        .and_then(|class| recovered.get(*class))
10834                        .map(|(owner, _, _, _)| owner.clone())
10835                        .or_else(|| scope.class_unit.clone());
10836                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10837                    recovered.push((
10838                        owner,
10839                        head.key.start_byte(),
10840                        head.opening.end_byte(),
10841                        Vec::new(),
10842                    ));
10843                    let class_index = recovered.len() - 1;
10844                    match head.folded_members {
10845                        // The ordinary shape leaves the aggregate open: every
10846                        // child that follows belongs to it until a close brace
10847                        // ends it.
10848                        None => open.push(class_index),
10849                        // A folded head carries its own member list and its own
10850                        // closing brace, so it closes here. The invocations that
10851                        // precede the class key are the dangling tail of the
10852                        // replacement that folded it, and belong to whatever
10853                        // aggregate is open around it, not to this one.
10854                        Some(members) => {
10855                            let macro_nodes = object_macro_identifier_nodes_with_environment(
10856                                children[index],
10857                                self.source,
10858                                &mut object_macro_fields,
10859                                &mut ambiguous_object_macro_fields,
10860                            );
10861                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10862                                .iter()
10863                                .partition(|node| node.start_byte() < head.key.start_byte());
10864                            if let Some(&enclosing) = open.last() {
10865                                for macro_node in preceding {
10866                                    recovered[enclosing]
10867                                        .3
10868                                        .push(CppCollapsedMember::MacroFields {
10869                                            context: macro_node,
10870                                            range: cpp_declaration_range(macro_node),
10871                                            fields: object_macro_field_closure(
10872                                                &object_macro_fields,
10873                                                &normalize_cpp_whitespace(node_text(
10874                                                    macro_node,
10875                                                    self.source,
10876                                                )),
10877                                            ),
10878                                        });
10879                                }
10880                            }
10881                            let closing = cpp_collapsed_aggregate_closing_brace(members);
10882                            recovered[class_index]
10883                                .3
10884                                .extend(cpp_collapsed_aggregate_members(
10885                                    members,
10886                                    &inner,
10887                                    head.opening.end_byte()..closing,
10888                                    head.opening.end_position().row + 1,
10889                                    self.source,
10890                                    &object_macro_fields,
10891                                ));
10892                            recovered[class_index].2 = members.end_byte();
10893                            for &open_class in &open {
10894                                recovered[open_class].2 =
10895                                    recovered[open_class].2.max(members.end_byte());
10896                            }
10897                        }
10898                    }
10899                    index += head.width;
10900                    continue;
10901                }
10902            }
10903            if let Some(&class_index) = open.last()
10904                && children[index].kind() == "field_declaration"
10905            {
10906                let field = children[index];
10907                let macro_nodes = object_macro_identifier_nodes_with_environment(
10908                    field,
10909                    self.source,
10910                    &mut object_macro_fields,
10911                    &mut ambiguous_object_macro_fields,
10912                );
10913                recovered[class_index]
10914                    .3
10915                    .extend(cpp_collapsed_aggregate_members(
10916                        field,
10917                        &macro_nodes,
10918                        field.start_byte()..field.end_byte(),
10919                        field.start_position().row + 1,
10920                        self.source,
10921                        &object_macro_fields,
10922                    ));
10923                let end = field.end_byte();
10924                for &open_class in &open {
10925                    recovered[open_class].2 = recovered[open_class].2.max(end);
10926                }
10927                let closes = count_close_brace_nodes(field);
10928                for _ in 0..closes {
10929                    if let Some(closed) = open.pop() {
10930                        recovered[closed].2 = end;
10931                    }
10932                }
10933            }
10934            index += 1;
10935        }
10936
10937        let mut owners = Vec::with_capacity(recovered.len());
10938        for (owner, start, end, members) in recovered {
10939            let parent = owners
10940                .iter()
10941                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
10942                .cloned()
10943                .or_else(|| scope.class_unit.clone());
10944            self.declare_collapsed_aggregate(
10945                owner.clone(),
10946                start..end,
10947                Some(node),
10948                parent,
10949                members,
10950                scope,
10951            );
10952            owners.push(owner);
10953        }
10954    }
10955
10956    /// Record one recovered aggregate and the members it declares.
10957    fn declare_collapsed_aggregate(
10958        &mut self,
10959        owner: CodeUnit,
10960        span: std::ops::Range<usize>,
10961        evidence: Option<Node<'_>>,
10962        parent: Option<CodeUnit>,
10963        members: Vec<CppCollapsedMember<'_>>,
10964        scope: &ScopeInfo,
10965    ) {
10966        let range = Range {
10967            start_byte: span.start,
10968            end_byte: span.end,
10969            start_line: self.source.get(..span.start).map_or(1, |source| {
10970                source.bytes().filter(|byte| *byte == b'\n').count() + 1
10971            }),
10972            end_line: self.source.get(..span.end).map_or(1, |source| {
10973                source.bytes().filter(|byte| *byte == b'\n').count() + 1
10974            }),
10975        };
10976        // The tag can already be declared by a typedef or a forward
10977        // declaration, and this range is the definition's. Record it either
10978        // way: a declaration carries every range it is written at, and
10979        // dropping this one leaves nothing able to prove that the aggregate
10980        // has a body (#3098). Recording a range that is already held, or a
10981        // unit that is already declared, changes nothing.
10982        self.add_declaration_with_range(
10983            self.primary_ancestry,
10984            owner.clone(),
10985            range,
10986            evidence,
10987            parent,
10988            None,
10989        );
10990        // The recovery admitted this owner from an aggregate head and body.
10991        // Its malformed outer node need not itself be a struct_specifier, so
10992        // retain that structured definition proof explicitly.
10993        let declaration = *self.source_links[&owner]
10994            .last()
10995            .expect("collapsed aggregate has a source occurrence");
10996        {
10997            let mut properties = self.source_properties.borrow_mut();
10998            let fact = properties
10999                .get_mut(&declaration)
11000                .expect("collapsed aggregate retains its original context");
11001            fact.class_strength =
11002                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11003            fact.occurrence_role =
11004                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11005        }
11006        let owner_scope = ScopeInfo {
11007            class_unit: Some(owner),
11008            declarations_are_fields: true,
11009            ..scope.clone()
11010        };
11011        for member in members {
11012            match member {
11013                CppCollapsedMember::MacroFields {
11014                    range,
11015                    fields,
11016                    context,
11017                } => {
11018                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11019                }
11020                CppCollapsedMember::Declarations {
11021                    span,
11022                    start_line,
11023                    context,
11024                } => {
11025                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11026                }
11027            }
11028        }
11029    }
11030
11031    /// Recover an aggregate whose head tree-sitter folded into the declaration
11032    /// that precedes it, outside any collapsed `ERROR` container.
11033    ///
11034    /// A comment inside a field-list macro's replacement ends the replacement
11035    /// token, so the rest of the replacement is read at file scope. When the
11036    /// aggregate that follows declares its member list with one invocation,
11037    /// the parser reads the dangling tail as that declaration's type, the
11038    /// class key as an `ERROR`, and the member list as an initializer (#3098).
11039    /// The invocations before the class key belong to the replacement, not to
11040    /// this aggregate, so they go to whatever aggregate encloses it.
11041    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11042        if node.parent().is_some_and(|parent| parent.is_error()) {
11043            // The container's own recovery reads this child in source order,
11044            // with the preprocessor environment its earlier children build.
11045            return false;
11046        }
11047        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11048            return false;
11049        };
11050        let members = head
11051            .folded_members
11052            .expect("a folded aggregate head carries its member list");
11053        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11054        if name.is_empty() {
11055            return false;
11056        }
11057        let macro_nodes =
11058            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11059        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11060            .iter()
11061            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11062        for macro_node in preceding {
11063            let fields = object_macro_field_closure(
11064                &self.object_macro_fields,
11065                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11066            );
11067            self.materialize_object_macro_fields(
11068                fields,
11069                cpp_declaration_range(macro_node),
11070                macro_node,
11071                scope,
11072            );
11073        }
11074        let parent = scope.class_unit.clone();
11075        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11076        let recovered = cpp_collapsed_aggregate_members(
11077            members,
11078            &inner,
11079            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11080            head.opening.end_position().row + 1,
11081            self.source,
11082            &self.object_macro_fields,
11083        );
11084        self.declare_collapsed_aggregate(
11085            owner,
11086            head.key.start_byte()..members.end_byte(),
11087            Some(node),
11088            parent,
11089            recovered,
11090            scope,
11091        );
11092        true
11093    }
11094
11095    /// Record the members of a collapsed aggregate region as fields of its
11096    /// recovered owner. The names, declaration text, and byte ranges all come
11097    /// from the grammar's reading of the region, so a region that is not a
11098    /// member list contributes nothing.
11099    fn record_collapsed_aggregate_fields(
11100        &mut self,
11101        span: std::ops::Range<usize>,
11102        start_line: usize,
11103        context: Node<'_>,
11104        scope: &ScopeInfo,
11105    ) {
11106        let Some(owner) = scope.class_unit.as_ref() else {
11107            return;
11108        };
11109        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11110            let range = Range {
11111                start_byte: field.range.start,
11112                end_byte: field.range.end,
11113                start_line: start_line
11114                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
11115                end_line: start_line
11116                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
11117            };
11118            let mut fq = owner.fq().clone();
11119            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11120            let short_name = if owner.short_name().is_empty() {
11121                field.name.clone()
11122            } else {
11123                format!("{}.{}", owner.short_name(), field.name)
11124            };
11125            let signature = normalize_cpp_whitespace(&field.declaration);
11126            let code_unit = CodeUnit::with_signature_and_fq(
11127                self.file.clone(),
11128                CodeUnitType::Field,
11129                owner.package_name().to_string(),
11130                short_name,
11131                Some(signature.clone()),
11132                false,
11133                fq,
11134            );
11135            if self.parsed.contains_declaration(&code_unit) {
11136                continue;
11137            }
11138            self.add_declaration_with_range(
11139                self.primary_ancestry,
11140                code_unit.clone(),
11141                range,
11142                None,
11143                Some(owner.clone()),
11144                None,
11145            );
11146            self.capture_recovered_field_facts(
11147                &code_unit,
11148                range,
11149                context,
11150                field.field_type,
11151                field.names_function_type,
11152            );
11153            self.parsed.add_signature(code_unit, signature);
11154        }
11155    }
11156
11157    fn visit_preproc_call(&mut self, node: Node<'_>) {
11158        let Some(_directive) = node.child_by_field_name("directive") else {
11159            return;
11160        };
11161        if is_cpp_undef_directive(node, self.source) {
11162            update_object_macro_field_environment(
11163                node,
11164                self.source,
11165                &mut self.object_macro_fields,
11166                &mut self.ambiguous_object_macro_fields,
11167            );
11168        }
11169    }
11170}
11171
11172/// Every member a field-list macro contributes under `environment`, including
11173/// the members of the field-list macros it composes.
11174///
11175/// Composition is resolved at the invocation, not at the definition, because a
11176/// nested name's active replacement is a property of the environment the
11177/// invocation sees. The traversal is iterative and visits each name once, so a
11178/// self-composing or mutually composing pair terminates.
11179fn object_macro_field_closure(
11180    environment: &HashMap<String, ObjectMacroReplacement>,
11181    name: &str,
11182) -> Vec<MacroReplacementField> {
11183    let mut fields = Vec::new();
11184    let mut visited = HashSet::default();
11185    let mut stack = vec![name.to_string()];
11186    while let Some(current) = stack.pop() {
11187        if !visited.insert(current.clone()) {
11188            continue;
11189        }
11190        let Some(replacement) = environment.get(&current) else {
11191            continue;
11192        };
11193        fields.extend(replacement.fields.iter().cloned());
11194        stack.extend(replacement.nested.iter().rev().cloned());
11195    }
11196    fields
11197}
11198
11199/// The structured replacement an object-like `#define` contributes.
11200///
11201/// The directive's whole logical line is the replacement list, which
11202/// [`object_macro_replacement_span`] recovers, because a comment inside the
11203/// replacement ends tree-sitter's `preproc_arg` token early.
11204fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11205    crate::graph::syntax::object_macro_replacement_span(node, source)
11206        .and_then(|span| source.get(span))
11207        .map(crate::graph::syntax::object_macro_replacement)
11208        .unwrap_or_default()
11209}
11210
11211/// Apply one preprocessor directive to an object-like field-list environment.
11212/// The environment is intentionally separate from the declaration visitor so
11213/// malformed parser regions can replay directives in source order without
11214/// mutating the live walk's state ahead of those directives.
11215fn update_object_macro_field_environment(
11216    node: Node<'_>,
11217    source: &str,
11218    fields: &mut HashMap<String, ObjectMacroReplacement>,
11219    ambiguous: &mut HashSet<String>,
11220) -> bool {
11221    match node.kind() {
11222        "preproc_def" => {
11223            let Some(name) = extract_macro_name(node, source) else {
11224                return false;
11225            };
11226            let replacement = object_macro_replacement_of(node, source);
11227            if replacement.is_empty() || ambiguous.contains(&name) {
11228                fields.remove(&name);
11229                ambiguous.insert(name);
11230            } else if let Some(previous) = fields.get(&name) {
11231                if previous != &replacement {
11232                    fields.remove(&name);
11233                    ambiguous.insert(name);
11234                }
11235            } else {
11236                fields.insert(name, replacement);
11237            }
11238            true
11239        }
11240        "preproc_call" if is_cpp_undef_directive(node, source) => {
11241            if let Some(argument) = node.child_by_field_name("argument") {
11242                let name = node_text(argument, source).trim();
11243                fields.remove(name);
11244                if inside_preprocessor_conditional(node) {
11245                    ambiguous.insert(name.to_string());
11246                } else {
11247                    ambiguous.remove(name);
11248                }
11249            }
11250            true
11251        }
11252        _ => false,
11253    }
11254}
11255
11256fn object_macro_identifier_nodes<'tree>(
11257    node: Node<'tree>,
11258    source: &str,
11259    fields: &HashMap<String, ObjectMacroReplacement>,
11260) -> Vec<Node<'tree>> {
11261    walk_object_macro_identifier_nodes(node, |current, result| {
11262        if matches!(
11263            current.kind(),
11264            "identifier" | "field_identifier" | "type_identifier"
11265        ) && fields.contains_key(node_text(current, source).trim())
11266        {
11267            result.push(current);
11268        }
11269        false
11270    })
11271}
11272
11273fn walk_object_macro_identifier_nodes<'tree>(
11274    node: Node<'tree>,
11275    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11276) -> Vec<Node<'tree>> {
11277    let mut result = Vec::new();
11278    let mut stack = vec![node];
11279    while let Some(current) = stack.pop() {
11280        if visit(current, &mut result) {
11281            continue;
11282        }
11283        let mut cursor = current.walk();
11284        let children = current.children(&mut cursor).collect::<Vec<_>>();
11285        stack.extend(children.into_iter().rev());
11286    }
11287    result.sort_by_key(|node| node.start_byte());
11288    result
11289}
11290
11291/// Collect macro identifiers while replaying any nested preprocessor
11292/// directives in source order. Recovery regions can contain a later `#undef`
11293/// in the same parser error envelope; using the live visitor map for the
11294/// entire envelope would incorrectly materialize invocations after it.
11295fn object_macro_identifier_nodes_with_environment<'tree>(
11296    node: Node<'tree>,
11297    source: &str,
11298    fields: &mut HashMap<String, ObjectMacroReplacement>,
11299    ambiguous: &mut HashSet<String>,
11300) -> Vec<Node<'tree>> {
11301    walk_object_macro_identifier_nodes(node, |current, result| {
11302        if update_object_macro_field_environment(current, source, fields, ambiguous) {
11303            return true;
11304        }
11305        if matches!(
11306            current.kind(),
11307            "identifier" | "field_identifier" | "type_identifier"
11308        ) && fields.contains_key(node_text(current, source).trim())
11309        {
11310            result.push(current);
11311        }
11312        false
11313    })
11314}
11315
11316/// One member region of an aggregate whose head and body tree-sitter collapsed
11317/// into an `ERROR` container, in source order.
11318enum CppCollapsedMember<'tree> {
11319    /// The members a field-list macro invocation donates, all sharing the
11320    /// invocation's range.
11321    MacroFields {
11322        context: Node<'tree>,
11323        range: Range,
11324        fields: Vec<MacroReplacementField>,
11325    },
11326    /// A byte span whose ordinary member declarations lost their shape with
11327    /// the aggregate body, with the 1-based line the span starts on.
11328    Declarations {
11329        context: Node<'tree>,
11330        span: std::ops::Range<usize>,
11331        start_line: usize,
11332    },
11333}
11334
11335/// One aggregate head recovered from the children of a collapsed `ERROR`
11336/// container.
11337///
11338/// A member list that opens with an object-like field-list macro invocation
11339/// has no grammar rule, so the aggregate never forms a specifier and its head
11340/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
11341/// class key, the name, and the opening brace are three consecutive children.
11342/// When the aggregate follows the dangling tail of a comment-truncated
11343/// replacement, the parser reads that tail as the declaration's type and folds
11344/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
11345/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
11346/// }` that follows becomes one `field_declaration` whose class key is an
11347/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
11348/// `initializer_list` (#3098).
11349struct CppCollapsedAggregateHead<'tree> {
11350    key: Node<'tree>,
11351    name: Node<'tree>,
11352    opening: Node<'tree>,
11353    /// The member list when the head folded it into its own node, which also
11354    /// carries the aggregate's closing brace. `None` leaves the aggregate open
11355    /// for the children that follow it.
11356    folded_members: Option<Node<'tree>>,
11357    /// How many children the head consumes.
11358    width: usize,
11359}
11360
11361/// The aggregate head that starts at `children[index]`, if one does.
11362fn cpp_collapsed_aggregate_head<'tree>(
11363    children: &[Node<'tree>],
11364    index: usize,
11365    source: &str,
11366) -> Option<CppCollapsedAggregateHead<'tree>> {
11367    let key = *children.get(index)?;
11368    if matches!(key.kind(), "struct" | "class" | "union") {
11369        let name = *children.get(index + 1)?;
11370        let opening = *children.get(index + 2)?;
11371        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11372            return None;
11373        }
11374        return Some(CppCollapsedAggregateHead {
11375            key,
11376            name,
11377            opening,
11378            folded_members: None,
11379            width: 3,
11380        });
11381    }
11382    cpp_folded_aggregate_head(key, source)
11383}
11384
11385/// The aggregate head tree-sitter folded into one declaration node, reading
11386/// the class key as an `ERROR` and the member list as an initializer.
11387fn cpp_folded_aggregate_head<'tree>(
11388    node: Node<'tree>,
11389    source: &str,
11390) -> Option<CppCollapsedAggregateHead<'tree>> {
11391    if !matches!(node.kind(), "declaration" | "field_declaration") {
11392        return None;
11393    }
11394    let mut cursor = node.walk();
11395    let children = node.children(&mut cursor).collect::<Vec<_>>();
11396    let key_index = children.iter().position(|child| {
11397        child.is_error()
11398            && matches!(
11399                node_text(*child, source).trim(),
11400                "struct" | "class" | "union"
11401            )
11402    })?;
11403    let declarator = *children.get(key_index + 1)?;
11404    // At file scope the grammar has an `init_declarator` for the name and its
11405    // initializer; inside an `ERROR` container the two are loose siblings.
11406    let (name, members) = if declarator.kind() == "init_declarator" {
11407        (
11408            declarator.child_by_field_name("declarator")?,
11409            declarator.child_by_field_name("value")?,
11410        )
11411    } else {
11412        (declarator, *children.get(key_index + 2)?)
11413    };
11414    if !matches!(
11415        name.kind(),
11416        "field_identifier" | "type_identifier" | "identifier"
11417    ) {
11418        return None;
11419    }
11420    if members.kind() != "initializer_list" {
11421        return None;
11422    }
11423    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11424    Some(CppCollapsedAggregateHead {
11425        key: children[key_index],
11426        name,
11427        opening,
11428        folded_members: Some(members),
11429        width: 1,
11430    })
11431}
11432
11433/// Where a folded member list ends: at its own closing brace when it has one,
11434/// so the reparse of the declarations it holds sees a member list and not a
11435/// stray brace.
11436fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11437    let mut cursor = members.walk();
11438    members
11439        .children(&mut cursor)
11440        .filter(|child| child.kind() == "}" && !child.is_missing())
11441        .last()
11442        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11443}
11444
11445/// The members one collapsed aggregate region declares, in source order: what
11446/// its field-list macro invocations donate, then the ordinary declarations
11447/// that follow the last invocation and lost their shape with the body.
11448fn cpp_collapsed_aggregate_members<'tree>(
11449    context: Node<'tree>,
11450    macro_nodes: &[Node<'tree>],
11451    region: std::ops::Range<usize>,
11452    region_start_line: usize,
11453    source: &str,
11454    environment: &HashMap<String, ObjectMacroReplacement>,
11455) -> Vec<CppCollapsedMember<'tree>> {
11456    let mut members = macro_nodes
11457        .iter()
11458        .map(|macro_node| CppCollapsedMember::MacroFields {
11459            context: *macro_node,
11460            range: cpp_declaration_range(*macro_node),
11461            fields: object_macro_field_closure(
11462                environment,
11463                &normalize_cpp_whitespace(node_text(*macro_node, source)),
11464            ),
11465        })
11466        .collect::<Vec<_>>();
11467    // Whatever follows the last invocation is an ordinary member list that
11468    // lost its declaration shape with the aggregate's body; the grammar
11469    // recovers it from its own byte range.
11470    let declarations_start = macro_nodes
11471        .last()
11472        .map_or(region.start, |macro_node| macro_node.end_byte());
11473    members.push(CppCollapsedMember::Declarations {
11474        context,
11475        span: declarations_start..region.end,
11476        start_line: region_start_line
11477            + cpp_line_breaks_between(source, region.start, declarations_start),
11478    });
11479    members
11480}
11481
11482/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
11483/// line without rescanning the file from its start.
11484fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11485    source.get(from..to).map_or(0, |slice| {
11486        slice.bytes().filter(|byte| *byte == b'\n').count()
11487    })
11488}
11489
11490/// How many enclosing aggregates a collapsed member declaration closes: the
11491/// close braces it carries that its own braces do not balance. A member that
11492/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
11493/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
11494/// owner early and lost every member after it (#2985).
11495fn count_close_brace_nodes(node: Node<'_>) -> usize {
11496    let mut opened = 0usize;
11497    let mut closed = 0usize;
11498    let mut stack = vec![node];
11499    while let Some(current) = stack.pop() {
11500        if !current.is_missing() {
11501            match current.kind() {
11502                "{" => opened += 1,
11503                "}" => closed += 1,
11504                _ => {}
11505            }
11506        }
11507        let mut cursor = current.walk();
11508        stack.extend(current.children(&mut cursor));
11509    }
11510    closed.saturating_sub(opened)
11511}
11512
11513/// Classify a C++ field while its declaration syntax is already available.
11514///
11515/// The persisted result lets later visibility queries avoid reparsing the
11516/// complete source file only to recover linkage.
11517pub fn cpp_field_declaration_linkage<'tree>(
11518    declaration: Node<'tree>,
11519    source: &str,
11520    ancestry: &ParentIndex<'tree>,
11521) -> CppFieldLinkage {
11522    let mut current = ancestry.parent(declaration);
11523    let mut enclosed_by_class = false;
11524    while let Some(node) = current {
11525        if node.kind() == "namespace_definition"
11526            && node
11527                .child_by_field_name("name")
11528                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11529        {
11530            return CppFieldLinkage::Internal;
11531        }
11532        if matches!(
11533            node.kind(),
11534            "class_specifier" | "struct_specifier" | "union_specifier"
11535        ) && node
11536            .child_by_field_name("name")
11537            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11538        {
11539            return CppFieldLinkage::Internal;
11540        }
11541        if matches!(
11542            node.kind(),
11543            "class_specifier" | "struct_specifier" | "union_specifier"
11544        ) {
11545            enclosed_by_class = true;
11546        }
11547        if matches!(node.kind(), "function_definition" | "lambda_expression") {
11548            return CppFieldLinkage::Internal;
11549        }
11550        current = ancestry.parent(node);
11551    }
11552    if enclosed_by_class {
11553        return CppFieldLinkage::External;
11554    }
11555    let mut cursor = declaration.walk();
11556    let mut has_static = false;
11557    let mut has_extern = false;
11558    let mut has_inline = false;
11559    let mut has_const = false;
11560    let mut has_constexpr = false;
11561    for child in declaration.named_children(&mut cursor) {
11562        let text = normalize_cpp_whitespace(node_text(child, source));
11563        match (child.kind(), text.as_str()) {
11564            ("storage_class_specifier", "static") => has_static = true,
11565            ("storage_class_specifier", "extern") => has_extern = true,
11566            ("storage_class_specifier", "inline") => has_inline = true,
11567            ("storage_class_specifier", "constexpr") => has_constexpr = true,
11568            ("type_qualifier", "const") => has_const = true,
11569            ("type_qualifier", "constexpr") => has_constexpr = true,
11570            _ => {}
11571        }
11572    }
11573    if has_static {
11574        CppFieldLinkage::Internal
11575    } else if has_extern || has_inline {
11576        CppFieldLinkage::External
11577    } else if has_const || has_constexpr {
11578        CppFieldLinkage::InternalUnlessExternalPeer
11579    } else {
11580        CppFieldLinkage::External
11581    }
11582}
11583
11584fn cpp_declaration_range(node: Node<'_>) -> Range {
11585    Range {
11586        start_byte: node.start_byte(),
11587        end_byte: node.end_byte(),
11588        start_line: node.start_position().row + 1,
11589        end_line: node.end_position().row + 1,
11590    }
11591}
11592
11593/// A recovery interval as a [`Range`], for materialization records whose
11594/// window is a byte region rather than one parser node (the sentinel-macro
11595/// region reparses, issue #941/#1657).
11596fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11597    let line_at = |byte: usize| {
11598        source.as_bytes()[..byte]
11599            .iter()
11600            .filter(|&&b| b == b'\n')
11601            .count()
11602            + 1
11603    };
11604    Range {
11605        start_byte,
11606        end_byte,
11607        start_line: line_at(start_byte),
11608        end_line: line_at(end_byte),
11609    }
11610}
11611
11612fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11613    ImportInfo {
11614        raw_snippet,
11615        is_wildcard: false,
11616        is_global: false,
11617        identifier: None,
11618        alias: None,
11619        path: None,
11620        binder_span: None,
11621    }
11622}
11623
11624fn recover_quoted_source_imports(
11625    source: &str,
11626    imports: &mut Vec<SourceImportFact>,
11627    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11628    collector: &RefCell<PrimarySourceFactCollector<'_>>,
11629) {
11630    let mut in_block_comment = false;
11631    let mut start = 0;
11632    for line in source.split_inclusive('\n') {
11633        let end = start + line.len();
11634        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11635        let trimmed = stripped.trim();
11636        if looks_like_quoted_include_line(trimmed) {
11637            let raw = normalize_cpp_whitespace(trimmed);
11638            if !imports.iter().any(|import| import.statement == raw) {
11639                let occurrence = collector.borrow_mut().intern_subspan_bytes(
11640                    start,
11641                    end,
11642                    SourceOccurrenceProvenance::ExplicitSubspan,
11643                );
11644                // Recovery already identified a quoted directive hidden by a
11645                // malformed outer construct. Interpret its path with the same
11646                // grammar in an isolated included range, preserving byte identity.
11647                let region =
11648                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11649                let target = region.as_ref().and_then(|tree| {
11650                    let mut pending = vec![tree.root_node()];
11651                    while let Some(node) = pending.pop() {
11652                        if node.kind() == "preproc_include"
11653                            && let Some(path) = node.child_by_field_name("path")
11654                            && path.kind() == "string_literal"
11655                        {
11656                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
11657                            let target = collector.borrow_mut().intern_embedded(
11658                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11659                            );
11660                            includes.push(
11661                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11662                                    declaration: occurrence,
11663                                    target,
11664                                    path: value.to_owned(),
11665                                    quoted: true,
11666                                },
11667                            );
11668                            return Some(target);
11669                        }
11670                        let mut cursor = node.walk();
11671                        let children: Vec<_> = node.named_children(&mut cursor).collect();
11672                        pending.extend(children.into_iter().rev());
11673                    }
11674                    None
11675                });
11676                imports.push(SourceImportFact::from_import(
11677                    cpp_include_info(raw),
11678                    occurrence,
11679                    target,
11680                    None,
11681                    Vec::new(),
11682                ));
11683            }
11684        }
11685        start = end;
11686    }
11687}
11688
11689fn looks_like_quoted_include_line(line: &str) -> bool {
11690    let Some(rest) = line.trim_start().strip_prefix('#') else {
11691        return false;
11692    };
11693    let Some(rest) = rest.trim_start().strip_prefix("include") else {
11694        return false;
11695    };
11696    rest.trim_start().starts_with('"')
11697}
11698
11699fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11700    let mut raw = Vec::new();
11701    if let Some(bases) = cpp_class_like_base_clause(node) {
11702        collect_cpp_base_nodes(bases, source, &mut raw);
11703    }
11704    raw
11705}
11706
11707fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11708    let mut facts = Vec::new();
11709    let mut cursor = node.walk();
11710    for child in node.named_children(&mut cursor) {
11711        if child.kind() != "base_class_clause" {
11712            continue;
11713        }
11714        let mut is_virtual = false;
11715        let mut child_cursor = child.walk();
11716        for base in child.children(&mut child_cursor) {
11717            match base.kind() {
11718                "virtual" => is_virtual = true,
11719                "," => is_virtual = false,
11720                "type_identifier"
11721                | "qualified_identifier"
11722                | "scoped_type_identifier"
11723                | "template_type"
11724                | "template_function" => {
11725                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
11726                        base,
11727                        source,
11728                        is_virtual,
11729                        crate::source_facts::base_access_specifier(child, base, node.kind()),
11730                    ) {
11731                        facts.push(fact);
11732                    }
11733                    is_virtual = false;
11734                }
11735                _ => {}
11736            }
11737        }
11738    }
11739    facts
11740}
11741
11742/// The `base_class_clause` a class-like node writes, if it writes one.
11743///
11744/// The pinned grammar exposes the clause as a named child rather than a field,
11745/// so the question is asked by kind. A base clause is the only place a C++
11746/// class body records that some other class may declare a member, which is
11747/// what [`cpp_callable_dispatch_extensibility`] reads it for.
11748fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11749    let mut cursor = class_like.walk();
11750    class_like
11751        .named_children(&mut cursor)
11752        .find(|child| child.kind() == "base_class_clause")
11753}
11754
11755fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11756    walk_named_tree_preorder(node, false, |child| match child.kind() {
11757        "type_identifier" | "qualified_identifier" | "template_type" => {
11758            let text = normalize_cpp_whitespace(node_text(child, source));
11759            if !text.is_empty() {
11760                raw.push(text);
11761            }
11762            WalkControl::SkipChildren
11763        }
11764        _ => WalkControl::Continue,
11765    });
11766}
11767
11768fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11769    let mut out = String::new();
11770    let chars: Vec<char> = line.chars().collect();
11771    let mut index = 0;
11772    let mut in_string = false;
11773    let mut in_char = false;
11774    let mut escape = false;
11775
11776    while index < chars.len() {
11777        let ch = chars[index];
11778        let next = chars.get(index + 1).copied();
11779
11780        if *in_block_comment {
11781            if ch == '*' && next == Some('/') {
11782                *in_block_comment = false;
11783                index += 2;
11784            } else {
11785                index += 1;
11786            }
11787            continue;
11788        }
11789
11790        if in_string {
11791            out.push(ch);
11792            if escape {
11793                escape = false;
11794            } else if ch == '\\' {
11795                escape = true;
11796            } else if ch == '"' {
11797                in_string = false;
11798            }
11799            index += 1;
11800            continue;
11801        }
11802
11803        if in_char {
11804            out.push(ch);
11805            if escape {
11806                escape = false;
11807            } else if ch == '\\' {
11808                escape = true;
11809            } else if ch == '\'' {
11810                in_char = false;
11811            }
11812            index += 1;
11813            continue;
11814        }
11815
11816        if ch == '/' && next == Some('/') {
11817            break;
11818        }
11819        if ch == '/' && next == Some('*') {
11820            *in_block_comment = true;
11821            index += 2;
11822            continue;
11823        }
11824        if ch == '"' {
11825            in_string = true;
11826            out.push(ch);
11827            index += 1;
11828            continue;
11829        }
11830        if ch == '\'' {
11831            in_char = true;
11832            out.push(ch);
11833            index += 1;
11834            continue;
11835        }
11836
11837        out.push(ch);
11838        index += 1;
11839    }
11840
11841    out
11842}
11843
11844#[derive(Clone)]
11845struct FunctionInfo {
11846    package_name: String,
11847    owner: Option<CppMemberOwner>,
11848    name: String,
11849    signature: String,
11850}
11851
11852/// Owner of a member function, kept structured so a literal `$` inside a
11853/// source-level class name never crosses a join/split boundary: the legacy
11854/// `$`-joined owner string was re-split at fq construction, dropping a leading
11855/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
11856/// and tripping the package/short boundary assert -- the #2140 corruption one
11857/// level up (#2362).
11858#[derive(Clone)]
11859enum CppMemberOwner {
11860    /// Source-level owner class chain from a qualified declarator-id, one
11861    /// class name per component (`Outer::Inner::method` -> `["Outer",
11862    /// "Inner"]`); each component may itself contain a literal `$`.
11863    Chain(Vec<String>),
11864    /// The lexically enclosing or recovered class unit; the member fq extends
11865    /// its fq directly instead of re-splitting its `$`-joined short chain.
11866    Unit(CodeUnit),
11867}
11868
11869impl CppMemberOwner {
11870    /// The legacy `$`-joined owner chain used in the member's short name.
11871    fn short_chain(&self) -> String {
11872        match self {
11873            Self::Chain(chain) => chain.join("$"),
11874            Self::Unit(parent) => parent.short_name().to_string(),
11875        }
11876    }
11877}
11878
11879enum DeclaratorKind<'a> {
11880    Function(Node<'a>),
11881    Variable(Node<'a>),
11882}
11883
11884impl FunctionInfo {
11885    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11886        self.code_unit_with_synthetic(file, false)
11887    }
11888
11889    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
11890        let short_name = match &self.owner {
11891            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
11892            None => self.name.clone(),
11893        };
11894        let fq = match &self.owner {
11895            Some(CppMemberOwner::Chain(chain)) => {
11896                debug_assert!(
11897                    !chain.is_empty(),
11898                    "an empty owner chain is no owner; producers return None instead"
11899                );
11900                let mut fq = FqName::new();
11901                cpp_push_package(&mut fq, &self.package_name);
11902                let mut first = true;
11903                for component in chain {
11904                    let kind = if first {
11905                        SegmentKind::Type
11906                    } else {
11907                        SegmentKind::Nested
11908                    };
11909                    fq.push(segment_interner().intern(component, kind));
11910                    first = false;
11911                }
11912                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11913                fq
11914            }
11915            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
11916                .fq()
11917                .clone()
11918                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
11919            // An anonymous parent (empty short chain) contributes no owner
11920            // segment -- the same guard as cpp_join_member_short above.
11921            Some(CppMemberOwner::Unit(_)) | None => {
11922                let mut fq = FqName::new();
11923                cpp_push_package(&mut fq, &self.package_name);
11924                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11925                fq
11926            }
11927        };
11928        CodeUnit::with_signature_and_fq(
11929            file,
11930            CodeUnitType::Function,
11931            self.package_name.clone(),
11932            short_name,
11933            Some(self.signature.clone()),
11934            synthetic,
11935            fq,
11936        )
11937    }
11938}
11939
11940fn extract_function_info(
11941    declarator: Node<'_>,
11942    source: &str,
11943    scope: &ScopeInfo,
11944) -> Option<FunctionInfo> {
11945    let parameters_node = declarator.child_by_field_name("parameters")?;
11946    let declarator_name_node = declarator
11947        .child_by_field_name("declarator")
11948        .or_else(|| parameters_node.prev_named_sibling())?;
11949    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
11950}
11951
11952fn extract_function_info_from_name(
11953    declarator: Node<'_>,
11954    declarator_name_node: Node<'_>,
11955    source: &str,
11956    scope: &ScopeInfo,
11957) -> Option<FunctionInfo> {
11958    let parameters_node = declarator.child_by_field_name("parameters")?;
11959    let parameters_text = cpp_parameter_signature(parameters_node, source);
11960    let recovered_specialization_member = scope
11961        .recovered_specialization_member_scope
11962        .then(|| {
11963            let terminal = declarator_name_node
11964                .child_by_field_name("name")
11965                .unwrap_or(declarator_name_node);
11966            let name = canonical_cpp_qualified_component(terminal, source)?.name;
11967            let owner = scope.class_unit.as_ref()?;
11968            Some((
11969                Some(CppMemberOwner::Unit(owner.clone())),
11970                name,
11971                scope.package_name.clone(),
11972            ))
11973        })
11974        .flatten();
11975    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
11976        parts
11977    } else if let Some(parts) =
11978        split_structured_templated_cpp_name(declarator_name_node, source, scope)
11979    {
11980        parts
11981    } else {
11982        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
11983            declarator_name_node,
11984            source,
11985        )?);
11986        if raw_name.is_empty() {
11987            return None;
11988        }
11989        let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
11990        match using_bound_cpp_owner(declarator_name_node, source, scope) {
11991            Some((owner_chain, package_name)) => {
11992                (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
11993            }
11994            None => (owner, name, package_name),
11995        }
11996    };
11997    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
11998    let mut signature = if suffix.is_empty() {
11999        parameters_text
12000    } else {
12001        format!("{parameters_text} {suffix}")
12002    };
12003    if let Some(template_signature) = &scope.template_signature {
12004        signature = format!("{template_signature}{signature}");
12005    }
12006
12007    Some(FunctionInfo {
12008        package_name,
12009        owner,
12010        name,
12011        signature,
12012    })
12013}
12014
12015/// Recover the semantic return type and callable name when a declaration macro
12016/// occupies a function definition's `type` field. Tree-sitter either exposes a
12017/// scalar return as the declarator's apparent name and the callable as the sole
12018/// identifier in an `ERROR`, or joins a template return and callable into a
12019/// qualified identifier with a missing `::`. Both shapes retain the complete
12020/// parameter list and body; a concrete separator remains an out-of-line member.
12021fn cpp_macro_displaced_callable_parts<'tree>(
12022    function_declarator: Node<'tree>,
12023    source: &str,
12024    ancestry: &ParentIndex<'tree>,
12025) -> Option<(Node<'tree>, Node<'tree>)> {
12026    let definition = ancestry.parent(function_declarator)?;
12027    if definition.kind() != "function_definition"
12028        || definition.child_by_field_name("declarator") != Some(function_declarator)
12029        || definition
12030            .child_by_field_name("body")
12031            .is_none_or(|body| body.kind() != "compound_statement")
12032    {
12033        return None;
12034    }
12035    let macro_type = definition.child_by_field_name("type")?;
12036    if macro_type.kind() != "type_identifier"
12037        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12038    {
12039        return None;
12040    }
12041
12042    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12043    if apparent_return_type.kind() == "qualified_identifier"
12044        && let (Some(return_type), Some(callable_name)) = (
12045            apparent_return_type.child_by_field_name("scope"),
12046            apparent_return_type.child_by_field_name("name"),
12047        )
12048        && return_type.kind() == "template_type"
12049        && matches!(callable_name.kind(), "identifier" | "field_identifier")
12050        && (0..apparent_return_type.child_count())
12051            .filter_map(|index| apparent_return_type.child(index))
12052            .any(|child| child.kind() == "::" && child.is_missing())
12053        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12054        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12055    {
12056        return Some((return_type, callable_name));
12057    }
12058    if !matches!(
12059        apparent_return_type.kind(),
12060        "identifier" | "field_identifier" | "type_identifier"
12061    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12062    {
12063        return None;
12064    }
12065    let parameters = function_declarator.child_by_field_name("parameters")?;
12066    let mut cursor = function_declarator.walk();
12067    let between = function_declarator
12068        .named_children(&mut cursor)
12069        .filter(|child| child.kind() != "comment")
12070        .filter(|child| {
12071            child.start_byte() >= apparent_return_type.end_byte()
12072                && child.end_byte() <= parameters.start_byte()
12073                && !same_node(*child, apparent_return_type)
12074                && !same_node(*child, parameters)
12075        })
12076        .collect::<Vec<_>>();
12077    let [name_error] = between.as_slice() else {
12078        return None;
12079    };
12080    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12081        return None;
12082    }
12083    let callable_name = name_error.named_child(0)?;
12084    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12085        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12086    {
12087        return None;
12088    }
12089    Some((apparent_return_type, callable_name))
12090}
12091
12092/// The part of a `function_declarator` after its parameter list that belongs to
12093/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
12094/// specification, a trailing return type and a trailing requires-clause.
12095///
12096/// The grammar makes each of these a distinct sibling of the `parameters`
12097/// field, so they are read from the tree. Splitting the declarator's text on
12098/// the parameter list instead silently dropped every qualifier whenever the
12099/// parameter list was spelled with whitespace that normalization rewrote - a
12100/// line break or a double space was enough to make a `const` member definition
12101/// a different logical symbol from its declaration (#1827).
12102///
12103/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
12104/// are deliberately excluded. C++ does not make them part of the signature and
12105/// an out-of-line definition never repeats them, so including them would split
12106/// a declaration from its own definition.
12107fn cpp_declarator_identity_suffix(
12108    declarator: Node<'_>,
12109    parameters_node: Node<'_>,
12110    source: &str,
12111) -> String {
12112    let mut cursor = declarator.walk();
12113    let parts = declarator
12114        .named_children(&mut cursor)
12115        .filter(|child| child.start_byte() >= parameters_node.end_byte())
12116        .filter(|child| {
12117            matches!(
12118                child.kind(),
12119                "type_qualifier"
12120                    | "ref_qualifier"
12121                    | "noexcept"
12122                    | "throw_specifier"
12123                    | "trailing_return_type"
12124                    | "requires_clause"
12125            )
12126        })
12127        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12128        .filter(|text| !text.is_empty())
12129        .collect::<Vec<_>>();
12130    normalize_cpp_qualifier_suffix(&parts.join(" "))
12131}
12132
12133/// The identity suffix of one callable declarator, for a consumer that holds
12134/// the declarator rather than the declaration walk's parts.
12135///
12136/// The persisted signature concatenates the parameter spelling and this suffix,
12137/// so a comparison that must agree on the suffix alone recomputes it here
12138/// instead of splitting the stored string.
12139pub(crate) fn cpp_callable_identity_suffix(
12140    function_declarator: Node<'_>,
12141    source: &str,
12142) -> Option<String> {
12143    let parameters_node = function_declarator.child_by_field_name("parameters")?;
12144    Some(cpp_declarator_identity_suffix(
12145        function_declarator,
12146        parameters_node,
12147        source,
12148    ))
12149}
12150
12151pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12152    match classify_declarator(node)? {
12153        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12154        DeclaratorKind::Variable(_) => None,
12155    }
12156}
12157
12158fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12159    match node.kind() {
12160        "function_declarator" => {
12161            let inner = node
12162                .child_by_field_name("declarator")
12163                .or_else(|| node.child_by_field_name("name"))
12164                .or_else(|| last_named_child(node));
12165            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12166                Some(DeclaratorKind::Variable(node))
12167            } else {
12168                Some(DeclaratorKind::Function(node))
12169            }
12170        }
12171        // A macro marker is declaration provenance, never a declarator: its
12172        // identifier names the macro, not anything the declaration declares.
12173        "macro_modifier" => None,
12174        "init_declarator"
12175        | "pointer_declarator"
12176        | "reference_declarator"
12177        | "parenthesized_declarator"
12178        | "array_declarator"
12179        | "attributed_declarator"
12180        | "template_function" => node
12181            .child_by_field_name("declarator")
12182            .or_else(|| node.child_by_field_name("name"))
12183            .or_else(|| last_named_child(node))
12184            .and_then(classify_declarator),
12185        "identifier" | "field_identifier" | "qualified_identifier" => {
12186            Some(DeclaratorKind::Variable(node))
12187        }
12188        _ => node
12189            .child_by_field_name("declarator")
12190            .or_else(|| node.child_by_field_name("name"))
12191            .or_else(|| last_named_child(node))
12192            .and_then(classify_declarator),
12193    }
12194}
12195
12196fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12197    matches!(
12198        node.kind(),
12199        "function_declarator"
12200            | "init_declarator"
12201            | "pointer_declarator"
12202            | "reference_declarator"
12203            | "parenthesized_declarator"
12204            | "array_declarator"
12205            | "attributed_declarator"
12206            | "template_function"
12207            | "macro_modifier_declarator"
12208            | "identifier"
12209            | "field_identifier"
12210            | "qualified_identifier"
12211    )
12212}
12213
12214fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12215    let class_like = first_class_like_child(node);
12216    let mut cursor = node.walk();
12217    node.named_children(&mut cursor).any(|child| {
12218        matches!(
12219            child.kind(),
12220            "init_declarator"
12221                | "pointer_declarator"
12222                | "reference_declarator"
12223                | "array_declarator"
12224                | "function_declarator"
12225                | "parenthesized_declarator"
12226                | "attributed_declarator"
12227        ) || matches!(
12228            child.kind(),
12229            "identifier" | "field_identifier" | "qualified_identifier"
12230        ) && class_like.is_none_or(|class_node| {
12231            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12232        })
12233    })
12234}
12235
12236/// One namespace-scope forward class declaration that a recovered export-macro
12237/// class definition may borrow its identity from.  Tree-sitter can close a
12238/// malformed class at the enclosing namespace's closing brace, leaving the later
12239/// class definitions as root-level recovered `function_definition` nodes.  A
12240/// preceding `class Name;` in the same namespace is the only structured identity
12241/// signal available in that shape.
12242///
12243/// Everything recorded here is a property of the forward declaration alone.
12244/// What depends on the node doing the asking -- that the forward and its
12245/// namespace both close before it, with nothing but recovery trivia between --
12246/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
12247/// tree answers every later question about it.
12248struct CppNamespaceForward {
12249    name: String,
12250    start_byte: usize,
12251    /// Where the malformed namespace that held the forward ended.  No query
12252    /// asks anything else about that node.
12253    namespace_end_byte: usize,
12254    package_name: String,
12255}
12256
12257/// Read `node` as a borrowable namespace forward declaration.
12258///
12259/// The admission is deliberately conservative: only a body-less class specifier
12260/// whose declaration has no declarator, at namespace scope rather than inside a
12261/// function or class body, in a namespace that itself failed to parse.
12262fn cpp_namespace_forward_entry<'tree>(
12263    node: Node<'tree>,
12264    source: &str,
12265    ancestry: &ParentIndex<'tree>,
12266) -> Option<CppNamespaceForward> {
12267    if !matches!(
12268        node.kind(),
12269        "class_specifier" | "struct_specifier" | "union_specifier"
12270    ) || cpp_body_node(node).is_some()
12271    {
12272        return None;
12273    }
12274    let parent = node.parent()?;
12275    if !(parent.kind() == "declaration_list"
12276        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12277    {
12278        return None;
12279    }
12280    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12281    // Borrowing is only justified by the parser-recovery shape we are
12282    // repairing: the namespace that held the forward must itself contain a
12283    // syntax error. A clean, unrelated namespace forward is not an identity
12284    // proof.
12285    if !namespace.has_error() {
12286        return None;
12287    }
12288    Some(CppNamespaceForward {
12289        name: class_like_name(node, source, ancestry)?,
12290        start_byte: node.start_byte(),
12291        namespace_end_byte: namespace.end_byte(),
12292        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12293    })
12294}
12295
12296/// Whether `forward` stands in the recovery relation to the node asking about
12297/// it: it and its malformed namespace both closed before the recovered class,
12298/// and nothing but recovery trivia separates the two.
12299fn cpp_namespace_forward_matches_recovery(
12300    forward: &CppNamespaceForward,
12301    recovered_node: Node<'_>,
12302) -> bool {
12303    forward.start_byte < recovered_node.start_byte()
12304        && forward.namespace_end_byte < recovered_node.start_byte()
12305        && malformed_namespace_is_nearest_recovery_region(
12306            forward.namespace_end_byte,
12307            recovered_node,
12308        )
12309}
12310
12311/// What one open [`CppVisitor::record_recovered_declarations`] has watched
12312/// happen to the declaration set.
12313///
12314/// The recovered set used to be a difference against a clone of the whole
12315/// declaration set, taken once per recovery: O(recoveries x declarations) on
12316/// exactly the error-recovered files that already walk slowest (#2787). The
12317/// walk knows which declarations it creates, so the capture collects them as
12318/// they are made and the difference is never needed.
12319///
12320/// `removed_pre_existing` is what makes that equal to the difference. A
12321/// deferred replacement removes the replaced declaration's children
12322/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
12323/// re-creates them. Creation alone cannot tell that apart from a first mint, so
12324/// a removal of a declaration this capture did not create records that it was
12325/// already there when the capture opened.
12326#[derive(Debug, Default)]
12327pub struct CppRecoveryCapture {
12328    /// Declarations created while this capture was open, in creation order.
12329    created: Vec<CodeUnit>,
12330    /// Membership for `created`.
12331    created_units: HashSet<CodeUnit>,
12332    /// Declarations that predate this capture and have been removed during it.
12333    removed_pre_existing: HashSet<CodeUnit>,
12334}
12335
12336/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
12337///
12338/// A region reparse is its own tree and is dropped while the walk that made it
12339/// continues, so a later parse can be allocated at the same address and hand out
12340/// the same node ids.  The root's span and shape pin the identity its address
12341/// alone does not: two roots agreeing on all of this are the same parse of the
12342/// same bytes, and a scan of one is a scan of the other.
12343#[derive(PartialEq, Eq, Hash)]
12344pub struct CppTreeIdentity {
12345    root_id: usize,
12346    start_byte: usize,
12347    end_byte: usize,
12348    kind_id: u16,
12349    child_count: usize,
12350}
12351
12352impl CppTreeIdentity {
12353    fn of(root: Node<'_>) -> Self {
12354        Self {
12355            root_id: root.id(),
12356            start_byte: root.start_byte(),
12357            end_byte: root.end_byte(),
12358            kind_id: root.kind_id(),
12359            child_count: root.child_count(),
12360        }
12361    }
12362}
12363
12364/// The namespace forward declarations one tree's prefix holds, folded in as the
12365/// walk asks about them.
12366///
12367/// `scope_for_recovered_exported_class` asks the same question once per
12368/// recovered class, and the answer depends only on the part of the tree that
12369/// starts before the asking node.  Rescanning that prefix per question is
12370/// quadratic in the file, and a generated header whose parse recovery leaves
12371/// thousands of class-like declarations at file scope pays all of it: 1,904
12372/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
12373/// (#2754).  This carries the scan forward instead.  Each question advances the
12374/// traversal from wherever the last one stopped to the asking node's start byte,
12375/// so a whole walk pays at most one pass over the prefix its furthest question
12376/// reaches, and each question then costs a name lookup.
12377#[derive(Default)]
12378pub struct CppNamespaceForwardScan {
12379    /// Every named node starting before this byte has been folded in.
12380    scanned_through: usize,
12381    forwards: HashMap<String, Vec<CppNamespaceForward>>,
12382}
12383
12384impl CppNamespaceForwardScan {
12385    /// Fold in every named node of `root` that starts at or after the fold
12386    /// watermark and before `cutoff`.
12387    ///
12388    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
12389    /// pass owes are exactly the ones no earlier pass reached, and a question
12390    /// about an earlier byte than one already answered costs nothing.
12391    fn advance_to<'tree>(
12392        &mut self,
12393        root: Node<'tree>,
12394        cutoff: usize,
12395        source: &str,
12396        ancestry: &ParentIndex<'tree>,
12397    ) {
12398        if cutoff <= self.scanned_through {
12399            return;
12400        }
12401        let folded_through = self.scanned_through;
12402        let mut cursor = root.walk();
12403        let mut stack = vec![root];
12404        while let Some(current) = stack.pop() {
12405            if (folded_through..cutoff).contains(&current.start_byte())
12406                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12407            {
12408                self.forwards
12409                    .entry(forward.name.clone())
12410                    .or_default()
12411                    .push(forward);
12412            }
12413            // A subtree ending before the watermark holds only nodes an earlier
12414            // pass already folded, and one starting at or after the cutoff is
12415            // outside the prefix being asked about. Skipping both is what keeps
12416            // the total traversal to one pass.
12417            for child in current.named_children(&mut cursor) {
12418                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12419                    stack.push(child);
12420                }
12421            }
12422        }
12423        self.scanned_through = cutoff;
12424    }
12425
12426    /// The one namespace `name` was forward declared in before `recovered_node`.
12427    /// More than one matching forward declaration is ambiguous and answers
12428    /// nothing rather than guessing.
12429    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12430        let mut matching = self
12431            .forwards
12432            .get(name)
12433            .into_iter()
12434            .flatten()
12435            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12436        let first = matching.next()?;
12437        matching
12438            .next()
12439            .is_none()
12440            .then(|| first.package_name.clone())
12441    }
12442}
12443
12444/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
12445/// debug build checks every answer against (and the one the parity tests drive
12446/// directly).  It walks the whole prefix per question, which is exactly the cost
12447/// #2754 removed from the release path.
12448#[cfg(any(debug_assertions, test))]
12449fn unique_earlier_cpp_namespace_forward<'tree>(
12450    recovered_node: Node<'tree>,
12451    name: &str,
12452    source: &str,
12453    ancestry: &ParentIndex<'tree>,
12454) -> Option<String> {
12455    let mut root = recovered_node;
12456    while let Some(parent) = ancestry.parent(root) {
12457        root = parent;
12458    }
12459
12460    let mut candidates = Vec::new();
12461    let mut stack = vec![root];
12462    while let Some(current) = stack.pop() {
12463        if current.start_byte() < recovered_node.start_byte()
12464            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12465            && forward.name == name
12466            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12467        {
12468            candidates.push(forward.package_name);
12469        }
12470
12471        let mut cursor = current.walk();
12472        for child in current.named_children(&mut cursor) {
12473            if child.start_byte() < recovered_node.start_byte() {
12474                stack.push(child);
12475            }
12476        }
12477    }
12478
12479    if candidates.len() == 1 {
12480        candidates.pop()
12481    } else {
12482        None
12483    }
12484}
12485
12486fn malformed_namespace_is_nearest_recovery_region(
12487    namespace_end_byte: usize,
12488    recovered_node: Node<'_>,
12489) -> bool {
12490    let mut root = recovered_node;
12491    while let Some(parent) = root.parent() {
12492        root = parent;
12493    }
12494    let mut cursor = root.walk();
12495    root.named_children(&mut cursor)
12496        .filter(|sibling| {
12497            namespace_end_byte <= sibling.start_byte()
12498                && sibling.end_byte() <= recovered_node.start_byte()
12499        })
12500        .all(is_malformed_namespace_recovery_trivia)
12501}
12502
12503fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12504    matches!(node.kind(), "ERROR" | "comment")
12505        || node.kind().starts_with("preproc_")
12506        || node.kind() == "expression_statement" && node.named_child_count() == 0
12507}
12508
12509/// Return the namespace path for a forward class only when the declaration is
12510/// at namespace scope.  A declaration nested in a function/class body may share
12511/// the same namespace ancestor but cannot identify a top-level class definition.
12512fn cpp_namespace_name_for_forward<'tree>(
12513    node: Node<'tree>,
12514    source: &str,
12515    ancestry: &ParentIndex<'tree>,
12516) -> Option<String> {
12517    cpp_namespace_definition_for_forward(node, ancestry)?;
12518    cpp_lexical_namespace_name(node, source)
12519}
12520
12521fn cpp_namespace_definition_for_forward<'tree>(
12522    node: Node<'tree>,
12523    ancestry: &ParentIndex<'tree>,
12524) -> Option<Node<'tree>> {
12525    let declaration = ancestry.parent(node)?;
12526    let mut ancestor = ancestry.parent(declaration);
12527    while let Some(current) = ancestor {
12528        if matches!(
12529            current.kind(),
12530            "compound_statement"
12531                | "field_declaration_list"
12532                | "class_specifier"
12533                | "struct_specifier"
12534                | "union_specifier"
12535                | "function_definition"
12536                | "lambda_expression"
12537        ) {
12538            return None;
12539        }
12540        if current.kind() == "namespace_definition" {
12541            return Some(current);
12542        }
12543        ancestor = ancestry.parent(current);
12544    }
12545    None
12546}
12547
12548fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12549    match node.kind() {
12550        "template_function" => node
12551            .child_by_field_name("name")
12552            .is_some_and(is_function_pointer_like_inner_declarator),
12553        _ => is_pointer_wrapper_declarator(node),
12554    }
12555}
12556
12557fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12558    match node.kind() {
12559        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12560        "parenthesized_declarator" => node
12561            .child_by_field_name("declarator")
12562            .or_else(|| last_named_child(node))
12563            .is_some_and(is_pointer_wrapper_declarator),
12564        _ => false,
12565    }
12566}
12567
12568/// The canonical owner chain and package of an out-of-line member whose
12569/// written qualifier starts with a name a `using ns::Type;` declaration bound
12570/// in the definition's own namespace scope (#6902 item 4).
12571///
12572/// `using imported::Type; void Type::method() {}` defines
12573/// `imported::Type::method`: the written `Type::` stays in the source, but the
12574/// member's identity follows the binding. The binding proves the owner only
12575/// when every visible binding of that name in this scope names the same
12576/// target. Conflicting bindings, a binding from an enclosing namespace (which
12577/// a same-named class in the inner namespace would shadow), and a using
12578/// declaration this walk cannot see (a preprocessor arm, another file) all
12579/// return `None`, and the caller keeps the written-qualifier reading.
12580fn using_bound_cpp_owner(
12581    declarator_name: Node<'_>,
12582    source: &str,
12583    scope: &ScopeInfo,
12584) -> Option<(Vec<String>, String)> {
12585    if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12586        return None;
12587    }
12588    let (components, explicitly_global) =
12589        structured_cpp_qualified_components(declarator_name, source)?;
12590    let (_, owner) = components.split_last()?;
12591    if explicitly_global
12592        || owner.is_empty()
12593        || components.iter().any(|component| component.is_template_id)
12594    {
12595        return None;
12596    }
12597    let mut targets = scope
12598        .visible_using_declarations
12599        .iter()
12600        .filter(|binding| {
12601            binding.scope_package == scope.package_name && binding.name == owner[0].name
12602        })
12603        .map(|binding| binding.target_package.as_str());
12604    let target_package = targets.next()?;
12605    if targets.any(|other| other != target_package) {
12606        return None;
12607    }
12608    Some((
12609        owner
12610            .iter()
12611            .map(|component| component.name.clone())
12612            .collect(),
12613        target_package.to_string(),
12614    ))
12615}
12616
12617fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12618    let cleaned = raw_name.trim_start_matches("template ").trim();
12619    // A leading `::` is the explicit-global marker, not an empty owner segment.
12620    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
12621    // erroneous macro envelope swallowing the first identifier of an
12622    // out-of-line `X::X` constructor, chromium #1573); without this strip the
12623    // split below yields owner_parts `[""]`, constructing a unit with an empty
12624    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
12625    let cleaned = cleaned.trim_start_matches("::");
12626    // Parser recovery can preserve two adjacent scope operators around a
12627    // missing component (for example `X::/**/::method` in compiler diagnostic
12628    // fixtures). Empty components are syntax-recovery artifacts, never C++
12629    // owners. Keeping one as the final owner constructed `short_name=".method"`
12630    // and violated the structured package/short boundary during a large LLVM
12631    // workspace build. This is the same legacy-string-to-FqName bridge as the
12632    // ordinary split above; discard only components that the delimiter itself
12633    // proves empty.
12634    let parts: Vec<_> = cleaned
12635        .split("::")
12636        .filter(|component| !component.is_empty())
12637        .collect();
12638    if parts.is_empty() {
12639        return (None, cleaned.to_string(), scope.package_name.clone());
12640    }
12641    if parts.len() > 1 {
12642        let name = parts.last().unwrap_or(&cleaned).to_string();
12643        let owner_parts = &parts[..parts.len() - 1];
12644        if let Some(class_unit) = &scope.class_unit {
12645            // Lexically inside a class body: the owner is that class, whatever
12646            // the declarator re-qualifies it as.
12647            return (
12648                Some(CppMemberOwner::Unit(class_unit.clone())),
12649                name,
12650                scope.package_name.clone(),
12651            );
12652        }
12653        if !scope.package_name.is_empty() {
12654            // Out-of-line member definition written *inside* an enclosing
12655            // `namespace {}` block (scope package is that namespace). Every
12656            // owner segment before the terminal member is a class-nesting step
12657            // -- an out-of-line nested-class member `Outer::Inner::method` in
12658            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
12659            // namespace path: `using namespace` never brings nested-class
12660            // access into unqualified scope, so C++ always writes the full
12661            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
12662            // that redundantly re-states the enclosing namespace it already
12663            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
12664            // strip that re-qualifying prefix (which duplicates a suffix of the
12665            // enclosing package path) before treating what remains as the
12666            // nested-class chain, so the redundant spelling still lands on the
12667            // same `log4cxx.Foo.method` identity as its header declaration.
12668            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12669            let owner = (!nested.is_empty()).then(|| {
12670                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12671            });
12672            return (owner, name, scope.package_name.clone());
12673        }
12674        // File scope (no enclosing `namespace {}` block, scope package empty).
12675        let (owner, package_name) = if owner_parts.len() > 1 {
12676            // A multi-segment qualifier at file scope with no enclosing
12677            // namespace: treat all but the last owner segment as the namespace
12678            // path and the last as the owning class (`ns1::ns2::Class::method`
12679            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
12680            // is really a namespace or an outer class cannot be told from the
12681            // declarator text alone here, and no enclosing namespace or
12682            // in-index owner is available at per-file extraction to confirm the
12683            // class reading, so the far-more-common namespace interpretation is
12684            // kept rather than guessed away (the nested-class-at-file-scope and
12685            // using-directive-qualified nested-class shapes remain on this
12686            // behavior; see #1121).
12687            (
12688                Some(CppMemberOwner::Chain(vec![
12689                    owner_parts.last().unwrap_or(&"").to_string(),
12690                ])),
12691                owner_parts[..owner_parts.len() - 1].join("::"),
12692            )
12693        } else {
12694            // A bare `Class::member` qualifier at file scope carries no
12695            // namespace segment of its own. The declarator alone cannot say
12696            // which namespace owns `Class` -- but a `using namespace X;`
12697            // directive already in effect at this point in the file (#1093,
12698            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
12699            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
12700            // is the remaining structural signal for it, so fall back to it
12701            // rather than leaving the definition's package empty while its
12702            // header declaration (parsed inside the `namespace {}` block) keeps
12703            // the real one -- an identity split that made the same member
12704            // unresolvable under its own displayed spelling.
12705            (
12706                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12707                cpp_using_directive_namespace_for_bare_owner(scope),
12708            )
12709        };
12710        return (owner, name, package_name);
12711    }
12712
12713    let package_name = scope.package_name.clone();
12714    let owner = scope
12715        .class_unit
12716        .as_ref()
12717        .map(|parent| CppMemberOwner::Unit(parent.clone()));
12718    (owner, cleaned.to_string(), package_name)
12719}
12720
12721/// Drop the leading owner segments of an out-of-line member qualifier that
12722/// merely re-state the enclosing namespace the definition already sits in, so
12723/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
12724/// definition may redundantly write `a::b::Outer::Inner::method` (or the
12725/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
12726/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
12727/// noise, not class-nesting steps. Returns the owner segments with the longest
12728/// such re-qualifying prefix removed (possibly all of them, when the qualifier
12729/// names only the enclosing namespace before the terminal member -- a
12730/// re-qualified free function). `package_name` is the enclosing namespace path
12731/// in its stored `::`-joined form; both sides are split on the same delimiter
12732/// the namespace walker joined them with, so this compares namespace *segments*
12733/// rather than scanning text.
12734fn strip_redundant_namespace_prefix<'a>(
12735    owner_parts: &'a [&'a str],
12736    package_name: &str,
12737) -> &'a [&'a str] {
12738    if package_name.is_empty() {
12739        return owner_parts;
12740    }
12741    let package_segments: Vec<&str> = package_name.split("::").collect();
12742    let max_prefix = owner_parts.len().min(package_segments.len());
12743    for prefix_len in (1..=max_prefix).rev() {
12744        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12745        if &owner_parts[..prefix_len] == package_suffix {
12746            return &owner_parts[prefix_len..];
12747        }
12748    }
12749    owner_parts
12750}
12751
12752/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
12753/// class name at file/namespace scope, from the `using namespace` directives
12754/// visible at this point in the file. Several may be in scope at once (a
12755/// primary `using namespace NS;` alongside deeper conveniences like `using
12756/// namespace NS::helpers;`); since the declarator gives no way to tell which
12757/// one actually declares the owner class, prefer the shallowest (fewest
12758/// `::`-separated segments) as the file's most likely "home" namespace,
12759/// breaking ties by declaration order. Returns an empty string (leaving the
12760/// caller's package unqualified, as before) when no using-namespace directive
12761/// is in scope.
12762fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12763    scope
12764        .visible_using_namespaces
12765        .iter()
12766        .min_by_key(|namespace| namespace.split("::").count())
12767        .cloned()
12768        .unwrap_or_default()
12769}
12770
12771struct CppQualifiedNameComponent {
12772    name: String,
12773    is_template_id: bool,
12774}
12775
12776/// Canonical nested-class chain for an out-of-line class definition written
12777/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
12778/// component per class (`["Outer", "Inner"]`).
12779///
12780/// The enclosing namespace fixes the namespace/class boundary: after an
12781/// optional redundant spelling of that namespace, every component belongs to
12782/// the class chain. File-scope qualified class names remain untouched because
12783/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
12784///
12785/// The components stay structured (rather than being `$`-joined here) so the
12786/// fq construction can push one Type/Nested segment per class; the `$`-joined
12787/// short-name display form is derived at the call sites that need it.
12788fn qualified_class_name_chain(
12789    class_node: Node<'_>,
12790    source: &str,
12791    scope: &ScopeInfo,
12792) -> Option<Vec<String>> {
12793    if scope.package_name.is_empty() || scope.class_unit.is_some() {
12794        return None;
12795    }
12796    let name = class_node.child_by_field_name("name")?;
12797    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12798    if explicitly_global
12799        || components.len() < 2
12800        || components.iter().any(|component| component.is_template_id)
12801    {
12802        return None;
12803    }
12804    let names = components
12805        .iter()
12806        .map(|component| component.name.as_str())
12807        .collect::<Vec<_>>();
12808    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12809    if class_chain.is_empty() {
12810        return None;
12811    }
12812    Some(class_chain.iter().map(|name| name.to_string()).collect())
12813}
12814
12815fn structured_cpp_qualified_components(
12816    qualified_name: Node<'_>,
12817    source: &str,
12818) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12819    if qualified_name.kind() != "qualified_identifier" {
12820        return None;
12821    }
12822
12823    let mut components = Vec::new();
12824    let mut current = qualified_name;
12825    let mut explicitly_global = false;
12826    loop {
12827        if current.kind() == "qualified_identifier" {
12828            if let Some(component) = current.child_by_field_name("scope") {
12829                components.push(canonical_cpp_qualified_component(component, source)?);
12830            } else if components.is_empty() {
12831                explicitly_global = true;
12832            } else {
12833                return None;
12834            }
12835            current = current.child_by_field_name("name")?;
12836        } else {
12837            components.push(canonical_cpp_qualified_component(current, source)?);
12838            break;
12839        }
12840    }
12841    Some((components, explicitly_global))
12842}
12843
12844fn split_structured_templated_cpp_name(
12845    declarator_name: Node<'_>,
12846    source: &str,
12847    scope: &ScopeInfo,
12848) -> Option<(Option<CppMemberOwner>, String, String)> {
12849    let (mut components, explicitly_global) =
12850        structured_cpp_qualified_components(declarator_name, source)?;
12851
12852    let terminal = components.pop()?;
12853    let owner_start = components
12854        .iter()
12855        .position(|component| component.is_template_id)?;
12856    let explicit_package = components[..owner_start]
12857        .iter()
12858        .map(|component| component.name.as_str())
12859        .collect::<Vec<_>>()
12860        .join("::");
12861    let explicit_package_is_empty = explicit_package.is_empty();
12862    let package_name = match (
12863        explicitly_global,
12864        scope.package_name.is_empty(),
12865        explicit_package_is_empty,
12866    ) {
12867        (true, _, _) => explicit_package,
12868        (false, _, true) => scope.package_name.clone(),
12869        (false, true, false) => explicit_package,
12870        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12871    };
12872    // Same identity-split fallback as `split_cpp_name` (#1093): a template
12873    // specialization's owner class named with no namespace segment of its own
12874    // (`explicit_package` empty) at file scope (`explicitly_global` false)
12875    // with nothing enclosing (`package_name` still empty) has no structural
12876    // signal for its namespace besides an in-scope `using namespace X;`.
12877    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12878    {
12879        cpp_using_directive_namespace_for_bare_owner(scope)
12880    } else {
12881        package_name
12882    };
12883    let owner_chain = components[owner_start..]
12884        .iter()
12885        .map(|component| component.name.clone())
12886        .collect::<Vec<_>>();
12887    if owner_chain.is_empty() || terminal.name.is_empty() {
12888        return None;
12889    }
12890
12891    Some((
12892        Some(CppMemberOwner::Chain(owner_chain)),
12893        terminal.name,
12894        package_name,
12895    ))
12896}
12897
12898fn canonical_cpp_qualified_component(
12899    mut component: Node<'_>,
12900    source: &str,
12901) -> Option<CppQualifiedNameComponent> {
12902    let mut is_template_id = false;
12903    loop {
12904        match component.kind() {
12905            "template_type" => {
12906                is_template_id = true;
12907                component = component.child_by_field_name("name")?;
12908            }
12909            "dependent_name" => component = component.named_child(0)?,
12910            "identifier"
12911            | "field_identifier"
12912            | "namespace_identifier"
12913            | "type_identifier"
12914            | "operator_name"
12915            | "destructor_name" => {
12916                let name = normalize_cpp_whitespace(node_text(component, source));
12917                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
12918                    name,
12919                    is_template_id,
12920                });
12921            }
12922            _ => component = component.child_by_field_name("name")?,
12923        }
12924    }
12925}
12926
12927fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
12928    if let Some(name) = macro_decorated_unqualified_name(node) {
12929        return extract_declarator_name(name, source);
12930    }
12931    match node.kind() {
12932        "identifier"
12933        | "field_identifier"
12934        | "type_identifier"
12935        | "operator_name"
12936        | "destructor_name"
12937        | "qualified_identifier" => node_text(node, source).to_string(),
12938        "function_declarator"
12939        | "pointer_declarator"
12940        | "reference_declarator"
12941        | "parenthesized_declarator"
12942        | "array_declarator"
12943        | "template_function" => node
12944            .child_by_field_name("declarator")
12945            .or_else(|| node.child_by_field_name("name"))
12946            .or_else(|| last_named_child(node))
12947            .map(|child| extract_declarator_name(child, source))
12948            .unwrap_or_else(|| node_text(node, source).to_string()),
12949        _ => node
12950            .child_by_field_name("name")
12951            .map(|child| extract_declarator_name(child, source))
12952            .unwrap_or_else(|| node_text(node, source).to_string()),
12953    }
12954}
12955
12956/// Extract a callable identity only through declaration-shaped AST nodes.
12957/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
12958/// expose the call's parameter list as a false function declarator; accepting
12959/// arbitrary node text there emitted bogus names such as `.*f`.
12960fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12961    if let Some(name) = macro_decorated_unqualified_name(node) {
12962        return extract_callable_declarator_name(name, source);
12963    }
12964    match node.kind() {
12965        "identifier"
12966        | "field_identifier"
12967        | "type_identifier"
12968        | "operator_name"
12969        | "destructor_name"
12970        | "qualified_identifier" => Some(node_text(node, source).to_string()),
12971        "function_declarator"
12972        | "pointer_declarator"
12973        | "reference_declarator"
12974        | "parenthesized_declarator"
12975        | "array_declarator"
12976        | "template_function" => node
12977            .child_by_field_name("declarator")
12978            .or_else(|| node.child_by_field_name("name"))
12979            .and_then(|child| extract_callable_declarator_name(child, source)),
12980        _ => None,
12981    }
12982}
12983
12984fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
12985    loop {
12986        if let Some(name) = macro_decorated_unqualified_name(node) {
12987            node = name;
12988            continue;
12989        }
12990        match node.kind() {
12991            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
12992                let name = node_text(node, source).trim().to_string();
12993                return (!name.is_empty()).then_some(name);
12994            }
12995            _ => {
12996                node = node
12997                    .child_by_field_name("declarator")
12998                    .or_else(|| node.child_by_field_name("name"))
12999                    .or_else(|| last_named_child(node))?;
13000            }
13001        }
13002    }
13003}
13004
13005/// Recover a C field whose name is wrapped in the function-like
13006/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
13007/// function declarator, while retaining its argument as a structured child.
13008/// The exact macro name and one-argument shape keep ordinary function-pointer
13009/// fields and unknown malformed declarators fail-closed.
13010#[derive(Clone, Copy)]
13011pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13012    pub(crate) name: Node<'tree>,
13013    pub(crate) declarator: Node<'tree>,
13014}
13015
13016impl RecoveredFunctionLikeFieldDeclarator<'_> {
13017    pub(crate) fn pointer_depth(self) -> i32 {
13018        let mut depth = 0;
13019        let mut current = self.declarator;
13020        while current.kind() != "function_declarator" {
13021            if current.kind() == "pointer_declarator" {
13022                depth += 1;
13023            }
13024            current = current
13025                .child_by_field_name("declarator")
13026                .expect("recovered field wrapper has an inner declarator");
13027        }
13028        depth
13029    }
13030}
13031
13032/// Whether a class-scope field declaration is really a function-like macro
13033/// invocation whose replacement the parser never saw.
13034///
13035/// The invocation carries no terminator of its own, so tree-sitter invents a
13036/// zero-width `;` and reads the macro name as the member's type and the
13037/// argument list as a `parenthesized_declarator`. That shape is not a member:
13038/// a member terminates itself, and with a real terminator the grammar reads
13039/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
13040/// type can name the macro being invoked; a builtin type keeps whatever
13041/// recovery the ordinary declarator walk makes of it.
13042fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13043    if node.kind() != "field_declaration" {
13044        return false;
13045    }
13046    let Some(type_node) = node.child_by_field_name("type") else {
13047        return false;
13048    };
13049    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13050        return false;
13051    }
13052    let Some(declarator) = node.child_by_field_name("declarator") else {
13053        return false;
13054    };
13055    if declarator.kind() != "parenthesized_declarator" {
13056        return false;
13057    }
13058    let mut invented_terminator = false;
13059    for child in children_iter(node) {
13060        if child.kind() != ";" {
13061            continue;
13062        }
13063        if !child.is_missing() {
13064            return false;
13065        }
13066        invented_terminator = true;
13067    }
13068    invented_terminator
13069}
13070
13071/// An access label that a class-scope function-like macro invocation
13072/// swallowed, as [`cpp_macro_swallowed_access_label`] reads it.
13073pub(crate) struct CppSwallowedAccessLabel<'tree> {
13074    /// The access keyword, which the grammar read as a `field_identifier`.
13075    pub(crate) keyword: Node<'tree>,
13076    /// `"public"`, `"protected"` or `"private"`.
13077    pub(crate) access: &'static str,
13078}
13079
13080/// Whether a class-body member is a function-like macro invocation that
13081/// swallowed the access label written after it, and with it the members under
13082/// that label.
13083///
13084/// `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)` carries no terminator of its own
13085/// (#3298). The macro-declaration grammar (brokk-tree-sitter-cpp 0.24.4) reads
13086/// the invocation as a `macro_type_specifier`, the `public` after it as the
13087/// member's `field_identifier` declarator, and the label's `:` as a bitfield
13088/// whose width expression shreds the next member, so the constructor written
13089/// under the label is left with no declarator at all. An access keyword is
13090/// never a declarator name, and a bitfield after it is how the parser spent
13091/// the label's colon, so the shape is the evidence. The access ladders read
13092/// the label from it too, because it still applies to every member after it.
13093pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13094    node: Node<'tree>,
13095    source: &str,
13096) -> Option<CppSwallowedAccessLabel<'tree>> {
13097    if !matches!(node.kind(), "field_declaration" | "function_definition") {
13098        return None;
13099    }
13100    node.child_by_field_name("type")
13101        .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13102    let keyword = node
13103        .child_by_field_name("declarator")
13104        .filter(|declarator| declarator.kind() == "field_identifier")?;
13105    let access = match node_text(keyword, source) {
13106        "public" => "public",
13107        "protected" => "protected",
13108        "private" => "private",
13109        _ => return None,
13110    };
13111    let mut clause = keyword.next_sibling()?;
13112    if clause.kind() == "ERROR" {
13113        clause = clause.child(0)?;
13114    }
13115    if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13116        return None;
13117    }
13118    Some(CppSwallowedAccessLabel { keyword, access })
13119}
13120
13121pub(crate) fn recovered_function_like_field_declarator<'tree>(
13122    node: Node<'tree>,
13123    source: &str,
13124) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13125    if node.kind() != "field_declaration" {
13126        return None;
13127    }
13128    let outer_declarator = node.child_by_field_name("declarator")?;
13129    let mut declarator = outer_declarator;
13130    while matches!(
13131        declarator.kind(),
13132        "pointer_declarator"
13133            | "reference_declarator"
13134            | "array_declarator"
13135            | "parenthesized_declarator"
13136    ) {
13137        declarator = declarator.child_by_field_name("declarator")?;
13138    }
13139    if declarator.kind() != "function_declarator" {
13140        return None;
13141    }
13142    let macro_name = declarator.child_by_field_name("declarator")?;
13143    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13144    {
13145        return None;
13146    }
13147    let parameters = declarator.child_by_field_name("parameters")?;
13148    let mut cursor = parameters.walk();
13149    let mut arguments = parameters.named_children(&mut cursor);
13150    let parameter = arguments.next()?;
13151    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13152        return None;
13153    }
13154    let name = parameter.child_by_field_name("type").filter(|argument| {
13155        matches!(
13156            argument.kind(),
13157            "identifier" | "field_identifier" | "type_identifier"
13158        )
13159    })?;
13160    Some(RecoveredFunctionLikeFieldDeclarator {
13161        name,
13162        declarator: outer_declarator,
13163    })
13164}
13165
13166use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13167
13168fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13169    let name_node = node.child_by_field_name("name")?;
13170    let name = normalize_cpp_whitespace(node_text(name_node, source));
13171    (!name.is_empty()).then_some(name)
13172}
13173
13174fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13175    if node.kind() != "declaration" {
13176        return Vec::new();
13177    }
13178    // The `typedef`/`using` keyword: a `type_identifier` in the type field,
13179    // or -- under the macro-declaration grammar, which reads the keyword as
13180    // an object-like macro -- the identifier a leading `macro_modifier`
13181    // wraps, with the aliased type in the type field.
13182    let keyword_is_alias = node
13183        .child_by_field_name("type")
13184        .filter(|node| {
13185            node.kind() == "type_identifier"
13186                && matches!(node_text(*node, source), "using" | "typedef")
13187        })
13188        .map(|keyword| node_text(keyword, source).to_owned())
13189        .or_else(|| {
13190            node.named_children(&mut node.walk())
13191                .find_map(macro_modifier_identifier)
13192                .map(|identifier| node_text(identifier, source).to_owned())
13193                .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13194        });
13195    let Some(keyword_is_alias) = keyword_is_alias else {
13196        return Vec::new();
13197    };
13198    let Some(declarator) = node.child_by_field_name("declarator") else {
13199        return Vec::new();
13200    };
13201    if keyword_is_alias == "using"
13202        && (declarator.kind() != "init_declarator"
13203            || declarator.child_by_field_name("value").is_none())
13204    {
13205        return Vec::new();
13206    }
13207    if keyword_is_alias == "typedef"
13208        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13209    {
13210        return vec![alias_name];
13211    }
13212    extract_typedef_declarator_name(declarator, source)
13213        .into_iter()
13214        .collect()
13215}
13216
13217/// The RHS retained by the same malformed alias shapes declaration admission
13218/// accepts. The alias name proves which declarator owns this source evidence.
13219pub(crate) fn recovered_alias_type_node<'tree>(
13220    node: Node<'tree>,
13221    alias_name: &str,
13222    source: &str,
13223) -> Option<Node<'tree>> {
13224    if node.kind() != "declaration" {
13225        return None;
13226    }
13227    let declarator = node.child_by_field_name("declarator")?;
13228    let admitted = recovered_type_alias_names(node, source)
13229        .iter()
13230        .any(|name| name == alias_name)
13231        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13232    if !admitted {
13233        return None;
13234    }
13235    if declarator.kind() == "init_declarator" {
13236        return declarator.child_by_field_name("value");
13237    }
13238    // The macro-declaration grammar keeps the aliased type in the type field
13239    // and the keyword as a `macro_modifier`; the alias name is the
13240    // declarator, so the type field is the RHS evidence.
13241    if node
13242        .named_children(&mut node.walk())
13243        .find_map(macro_modifier_identifier)
13244        .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13245    {
13246        return node.child_by_field_name("type");
13247    }
13248    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13249        .then_some(declarator)
13250}
13251
13252fn recovered_typedef_error_alias_name(
13253    declaration: Node<'_>,
13254    declarator: Node<'_>,
13255    source: &str,
13256) -> Option<String> {
13257    // An export macro between `class` and its name can make tree-sitter parse
13258    // the recovered class body as a function body. In that shape,
13259    //
13260    //     typedef spi::Filter BASE_CLASS;
13261    //
13262    // becomes a declaration whose `declarator` is the underlying qualified
13263    // type (`spi::Filter`) and whose actual alias name is displaced into the
13264    // following ERROR node. Do not publish the terminal underlying type
13265    // (`Filter`) as a false class-owned alias.
13266    if declarator.kind() != "qualified_identifier" {
13267        return None;
13268    }
13269    let mut cursor = declaration.walk();
13270    let mut errors = declaration
13271        .named_children(&mut cursor)
13272        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13273    let error = errors.next()?;
13274    if errors.next().is_some() || error.named_child_count() != 1 {
13275        return None;
13276    }
13277    let name = error.named_child(0)?;
13278    if !matches!(
13279        name.kind(),
13280        "identifier" | "field_identifier" | "type_identifier"
13281    ) {
13282        return None;
13283    }
13284    let name = normalize_cpp_whitespace(node_text(name, source));
13285    (!name.is_empty()).then_some(name)
13286}
13287
13288fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13289    // A function-like token in the type position can make tree-sitter expose
13290    // its argument as a parenthesized declarator. Do not publish that argument
13291    // as an alias. The macro-specific recovery below handles the proven shape.
13292    if fragmented_parenthesized_typedef_type(node).is_some() {
13293        return Vec::new();
13294    }
13295    let has_function_like_macro_type = node
13296        .child_by_field_name("type")
13297        .filter(|type_node| type_node.kind() == "type_identifier")
13298        .is_some_and(|type_node| {
13299            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13300        });
13301    let mut names = Vec::new();
13302    let mut cursor = node.walk();
13303    for declarator in node.children_by_field_name("declarator", &mut cursor) {
13304        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13305            continue;
13306        }
13307        if let Some(name) = extract_typedef_declarator_name(declarator, source)
13308            && !names.contains(&name)
13309        {
13310            names.push(name);
13311        }
13312    }
13313    names
13314}
13315
13316struct RecoveredMacroTypedefAlias<'tree> {
13317    name: String,
13318    end_node: Node<'tree>,
13319}
13320
13321/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
13322/// into an identifier expression statement. The uppercase macro token, missing
13323/// typedef terminator, and complete sibling terminator prove this exact shape.
13324fn recovered_macro_typedef_alias<'tree>(
13325    node: Node<'tree>,
13326    source: &str,
13327) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13328    let type_node = fragmented_parenthesized_typedef_type(node)?;
13329    if type_node.kind() != "type_identifier"
13330        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13331    {
13332        return None;
13333    }
13334
13335    let end_node = node.next_named_sibling()?;
13336    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13337        return None;
13338    }
13339    let name_node = end_node.named_child(0)?;
13340    if name_node.kind() != "identifier" {
13341        return None;
13342    }
13343    let has_terminator = (0..end_node.child_count()).any(|index| {
13344        end_node
13345            .child(index)
13346            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13347    });
13348    if !has_terminator {
13349        return None;
13350    }
13351    let name = normalize_cpp_whitespace(node_text(name_node, source));
13352    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13353}
13354
13355fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13356    if node.kind() != "type_definition" {
13357        return None;
13358    }
13359    let mut declarator_cursor = node.walk();
13360    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13361    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13362        return None;
13363    }
13364    let has_missing_terminator = (0..node.child_count()).any(|index| {
13365        node.child(index)
13366            .is_some_and(|child| child.kind() == ";" && child.is_missing())
13367    });
13368    if !has_missing_terminator {
13369        return None;
13370    }
13371    node.child_by_field_name("type")
13372}
13373
13374pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13375    let mut stack = vec![node];
13376    while let Some(node) = stack.pop() {
13377        let child = match node.kind() {
13378            "identifier" | "field_identifier" | "type_identifier" => {
13379                return direct_identifier_name(node, source);
13380            }
13381            "qualified_identifier" => node.child_by_field_name("name")?,
13382            _ => node
13383                .child_by_field_name("declarator")
13384                .or_else(|| node.child_by_field_name("name"))
13385                .or_else(|| last_named_child(node))?,
13386        };
13387        stack.push(child);
13388    }
13389    None
13390}
13391
13392fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13393    if let Some(name_node) = node.child_by_field_name("name") {
13394        let name = normalize_cpp_whitespace(node_text(name_node, source));
13395        return (!name.is_empty()).then_some(name);
13396    }
13397    let mut cursor = node.walk();
13398    node.named_children(&mut cursor)
13399        .find(|child| {
13400            matches!(
13401                child.kind(),
13402                "identifier" | "field_identifier" | "type_identifier"
13403            )
13404        })
13405        .and_then(|name_node| direct_identifier_name(name_node, source))
13406}
13407
13408fn render_cpp_type_signature(
13409    node: Node<'_>,
13410    source: &str,
13411    template_signature: Option<&str>,
13412) -> String {
13413    let text = normalize_cpp_whitespace(node_text(node, source));
13414    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13415    let rendered = if head.ends_with(';') {
13416        head.to_string()
13417    } else {
13418        format!("{head} {{")
13419    };
13420    if let Some(template_signature) = template_signature {
13421        format!("template {template_signature} {rendered}")
13422    } else {
13423        rendered
13424    }
13425}
13426
13427fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13428    if let Some(recovered) = recovered_pyobject_head_field(node, source)
13429        && recovered.declarator == declarator
13430    {
13431        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13432        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13433        return format!("{type_text} {declarator};");
13434    }
13435    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13436        && recovered.name == declarator
13437    {
13438        let type_text = node
13439            .child_by_field_name("type")
13440            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13441            .unwrap_or_default();
13442        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13443        let mut prefix = String::new();
13444        let mut suffix = String::new();
13445        let mut current = recovered.declarator;
13446        while current.kind() != "function_declarator" {
13447            match current.kind() {
13448                "pointer_declarator" => prefix.push('*'),
13449                "reference_declarator" => prefix.push('&'),
13450                "array_declarator" => {
13451                    let size = current
13452                        .child_by_field_name("size")
13453                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13454                        .unwrap_or_default();
13455                    suffix.push('[');
13456                    suffix.push_str(&size);
13457                    suffix.push(']');
13458                }
13459                "parenthesized_declarator" => {}
13460                _ => unreachable!("validated recovered field declarator wrapper"),
13461            }
13462            current = current
13463                .child_by_field_name("declarator")
13464                .expect("recovered field wrapper has an inner declarator");
13465        }
13466        let separator = if prefix.is_empty() { "" } else { " " };
13467        return format!("{type_text} {prefix}{separator}{name}{suffix};");
13468    }
13469    if let Some(signature) =
13470        render_recovered_macro_qualified_field_signature(node, declarator, source)
13471    {
13472        return signature;
13473    }
13474    let prefix = cpp_declaration_prefix(node, source);
13475    let name = extract_variable_name(declarator, source).unwrap_or_default();
13476    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13477    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13478        || (prefix.ends_with('&') && raw_suffix == "&")
13479    {
13480        String::new()
13481    } else {
13482        raw_suffix
13483    };
13484
13485    let mut rendered = if suffix.is_empty() {
13486        format!("{prefix} {name}")
13487    } else if suffix.starts_with('*') || suffix.starts_with('&') {
13488        format!("{prefix}{suffix} {name}")
13489    } else if suffix.starts_with('[') || suffix.starts_with('(') {
13490        format!("{prefix} {name}{suffix}")
13491    } else {
13492        format!("{prefix} {suffix}{name}")
13493    };
13494    rendered = collapse_cpp_whitespace(&rendered);
13495
13496    // Check the terminal token directly instead of normalizing the whole
13497    // declaration, which can copy a large initializer for each field.
13498    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13499        format!("{rendered} = {initializer};")
13500    } else if cpp_direct_semicolon(node).is_some() {
13501        format!("{rendered};")
13502    } else {
13503        rendered
13504    }
13505}
13506
13507fn render_recovered_macro_qualified_field_signature(
13508    node: Node<'_>,
13509    declarator: Node<'_>,
13510    source: &str,
13511) -> Option<String> {
13512    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13513    if !recovered
13514        .iter()
13515        .any(|candidate| same_node(*candidate, declarator))
13516    {
13517        return None;
13518    }
13519    let pseudo_declarator = node.child_by_field_name("declarator")?;
13520    let mut cursor = node.walk();
13521    let clause = node
13522        .named_children(&mut cursor)
13523        .find(|child| child.kind() == "bitfield_clause")?;
13524    let mut cursor = clause.walk();
13525    let error = clause
13526        .named_children(&mut cursor)
13527        .find(|child| child.kind() == "ERROR")?;
13528    let qualified_type =
13529        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13530    let prefix = cpp_declaration_prefix(node, source);
13531    let name = extract_variable_name(declarator, source)?;
13532    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13533    let mut rendered = if suffix.is_empty() {
13534        format!("{prefix} {qualified_type} {name}")
13535    } else {
13536        format!("{prefix} {qualified_type} {suffix} {name}")
13537    };
13538    rendered = collapse_cpp_whitespace(&rendered);
13539
13540    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13541        Some(format!(
13542            "{rendered} = {};",
13543            normalize_cpp_whitespace(node_text(initializer, source))
13544        ))
13545    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13546        Some(format!("{rendered} = {initializer};"))
13547    } else {
13548        Some(format!("{rendered};"))
13549    }
13550}
13551
13552fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13553    match node.kind() {
13554        "pointer_expression" => {
13555            let operator = node
13556                .child_by_field_name("operator")
13557                .or_else(|| node.child(0))
13558                .map(|operator| node_text(operator, source))
13559                .unwrap_or("*");
13560            let argument = node
13561                .child_by_field_name("argument")
13562                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13563                .unwrap_or_default();
13564            format!("{operator}{argument}")
13565        }
13566        "unary_expression" => {
13567            let operator = node
13568                .child_by_field_name("operator")
13569                .or_else(|| node.child(0))
13570                .map(|operator| node_text(operator, source))
13571                .unwrap_or_default();
13572            let argument = node
13573                .child_by_field_name("argument")
13574                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13575                .unwrap_or_default();
13576            format!("{operator}{argument}")
13577        }
13578        "identifier" | "field_identifier" => String::new(),
13579        _ => cpp_declarator_suffix_without_name(node, source),
13580    }
13581}
13582
13583fn recovered_macro_qualified_field_initializer<'tree>(
13584    clause: Node<'tree>,
13585    declarator: Node<'tree>,
13586) -> Option<Node<'tree>> {
13587    let mut stack = vec![clause];
13588    while let Some(current) = stack.pop() {
13589        if current.kind() == "assignment_expression"
13590            && current
13591                .child_by_field_name("left")
13592                .is_some_and(|left| same_node(left, declarator))
13593        {
13594            return current.child_by_field_name("right");
13595        }
13596        let mut cursor = current.walk();
13597        stack.extend(current.named_children(&mut cursor));
13598    }
13599    None
13600}
13601
13602fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13603    let text = node_text(node, source);
13604    let mut cursor = node.walk();
13605    let first_declarator = node.named_children(&mut cursor).find(|child| {
13606        matches!(
13607            child.kind(),
13608            "init_declarator"
13609                | "identifier"
13610                | "field_identifier"
13611                | "pointer_declarator"
13612                | "reference_declarator"
13613                | "array_declarator"
13614                | "function_declarator"
13615        )
13616    });
13617    let prefix = if let Some(first_declarator) = first_declarator {
13618        let end = first_declarator
13619            .start_byte()
13620            .saturating_sub(node.start_byte());
13621        let mut prefix = text.get(..end).unwrap_or(text).to_string();
13622        let declarator_suffix = match first_declarator.kind() {
13623            "init_declarator" => first_declarator
13624                .child_by_field_name("declarator")
13625                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13626                .unwrap_or_default(),
13627            _ => cpp_declarator_suffix_without_name(first_declarator, source),
13628        };
13629        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13630            prefix.push_str(&declarator_suffix);
13631        }
13632        return collapse_cpp_whitespace(&prefix)
13633            .trim_end_matches(',')
13634            .trim_end_matches(';')
13635            .trim()
13636            .to_string();
13637    } else {
13638        text
13639    };
13640    collapse_cpp_whitespace(prefix)
13641        .trim_end_matches(',')
13642        .trim_end_matches(';')
13643        .trim()
13644        .to_string()
13645}
13646
13647fn cpp_preserved_initializer(
13648    declaration_node: Node<'_>,
13649    declarator: Node<'_>,
13650    source: &str,
13651) -> Option<String> {
13652    let name = extract_variable_name(declarator, source)?;
13653    let mut cursor = declaration_node.walk();
13654    for child in declaration_node.named_children(&mut cursor) {
13655        if child.kind() != "init_declarator" {
13656            continue;
13657        }
13658        let Some(inner) = child.child_by_field_name("declarator") else {
13659            continue;
13660        };
13661        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13662            continue;
13663        }
13664        let value = child.child_by_field_name("value")?;
13665        let kind = value.kind();
13666        if matches!(
13667            kind,
13668            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13669        ) {
13670            return Some(normalize_cpp_whitespace(node_text(value, source)));
13671        }
13672        break;
13673    }
13674    // The current grammar keeps a member initializer as a `default_value`
13675    // field beside the declarator rather than inside an `init_declarator`.
13676    if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13677        && let Some(value) = equals.next_sibling().filter(|node| {
13678            declaration_node
13679                .child_by_field_name("default_value")
13680                .is_some_and(|default| same_node(default, *node))
13681        })
13682    {
13683        let kind = value.kind();
13684        if matches!(
13685            kind,
13686            "number_literal"
13687                | "float_literal"
13688                | "char_literal"
13689                | "true"
13690                | "false"
13691                | "null"
13692                | "nullptr"
13693                | "identifier"
13694        ) {
13695            return Some(normalize_cpp_whitespace(node_text(value, source)));
13696        }
13697    }
13698    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13699    let pattern = format!(
13700        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13701        regex::escape(&name)
13702    );
13703    Regex::new(&pattern)
13704        .ok()
13705        .and_then(|regex| regex.captures(&declaration_text))
13706        .and_then(|captures| captures.get(1))
13707        .map(|value| value.as_str().to_string())
13708}
13709
13710fn render_cpp_function_display_signature_from_node<'tree>(
13711    node: Node<'tree>,
13712    source: &str,
13713    template_signature: Option<&str>,
13714    has_body: bool,
13715    ancestry: &ParentIndex<'tree>,
13716) -> String {
13717    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13718    let parent_text = node_text(root, source);
13719    let body_local_start = root
13720        .child_by_field_name("body")
13721        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13722        .unwrap_or(parent_text.len());
13723    let display = parent_text
13724        .get(..body_local_start)
13725        .unwrap_or(parent_text)
13726        .trim()
13727        .trim();
13728    let display = if let Some(template_signature) = template_signature {
13729        if display.starts_with("template ") {
13730            display.to_string()
13731        } else {
13732            format!("template {template_signature} {display}")
13733        }
13734    } else {
13735        display.to_string()
13736    };
13737    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13738    if has_body {
13739        format!("{display} {{...}}")
13740    } else {
13741        format!("{display};")
13742    }
13743}
13744
13745fn cpp_template_signature(
13746    template_node: Node<'_>,
13747    declaration_child: Node<'_>,
13748    source: &str,
13749) -> Option<String> {
13750    let text = source
13751        .get(template_node.start_byte()..declaration_child.start_byte())
13752        .unwrap_or("");
13753    let text = normalize_cpp_whitespace(text);
13754    let start = text.find('<')?;
13755    let end = text.rfind('>')?;
13756    if end < start {
13757        return None;
13758    }
13759    Some(text[start..=end].to_string())
13760}
13761
13762struct RecoveredFragmentedPartialSpecialization<'tree> {
13763    declaration_node: Node<'tree>,
13764    name: String,
13765    range: Range,
13766    prefix_members: Vec<Node<'tree>>,
13767    member_siblings: Vec<Node<'tree>>,
13768    following_declarations: Vec<Node<'tree>>,
13769}
13770
13771struct RecoveredFragmentedPreprocessorClass<'tree> {
13772    declaration_node: Node<'tree>,
13773    class_node: Node<'tree>,
13774    body: Node<'tree>,
13775    name: String,
13776    range: Range,
13777    tail_members: Vec<Node<'tree>>,
13778    member_siblings: Vec<Node<'tree>>,
13779}
13780
13781/// Recover a class whose preprocessor-fragmented parse closes at an early
13782/// member body and publishes the remaining in-class declarations as siblings
13783/// of the surrounding alternative. Primary classes are admitted only when an
13784/// earlier branch contains the matching bodyless declaration and the class
13785/// node retains the displaced `#endif`. Partial specializations instead carry
13786/// their identity structurally in the `template_type` name and template
13787/// metadata. Retain the original AST nodes and re-own only the siblings through
13788/// the displaced structural `};` terminator.
13789fn recover_fragmented_preprocessor_class<'tree>(
13790    template_node: Node<'tree>,
13791    source: &str,
13792    ancestry: &ParentIndex<'tree>,
13793) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13794    let alternative = ancestry.parent(template_node)?;
13795    if alternative.kind() != "preproc_else" {
13796        return None;
13797    }
13798    let conditional = alternative.parent()?;
13799    if conditional.kind() != "preproc_if" {
13800        return None;
13801    }
13802    let declaration_node = template_node
13803        .named_children(&mut template_node.walk())
13804        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13805    let class_node = declaration_node
13806        .named_children(&mut declaration_node.walk())
13807        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13808    let body = cpp_body_node(class_node)?;
13809    if class_node.end_byte() >= declaration_node.end_byte() {
13810        return None;
13811    }
13812    let name = class_like_name(class_node, source, ancestry)?;
13813    let is_partial_specialization = class_node
13814        .child_by_field_name("name")
13815        .is_some_and(|class_name| class_name.kind() == "template_type");
13816    if is_partial_specialization {
13817        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13818        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13819            return None;
13820        }
13821    } else {
13822        if !class_has_displaced_preprocessor_terminator(class_node) {
13823            return None;
13824        }
13825        let matching_other_branch = conditional
13826            .named_children(&mut conditional.walk())
13827            .take_while(|child| !same_node(*child, alternative))
13828            .filter(|child| child.kind() == "template_declaration")
13829            .filter_map(first_class_like_child)
13830            .any(|candidate| {
13831                cpp_body_node(candidate).is_none()
13832                    && class_like_name(candidate, source, ancestry).as_deref()
13833                        == Some(name.as_str())
13834            });
13835        if !matching_other_branch {
13836            return None;
13837        }
13838    }
13839
13840    let mut tail_members = Vec::new();
13841    let mut saw_class = false;
13842    let mut declaration_cursor = declaration_node.walk();
13843    for child in declaration_node.named_children(&mut declaration_cursor) {
13844        if same_node(child, class_node) {
13845            saw_class = true;
13846        } else if saw_class {
13847            tail_members.push(child);
13848        }
13849    }
13850
13851    let mut member_siblings = Vec::new();
13852    let mut saw_template = false;
13853    let mut terminator = None;
13854    for index in 0..alternative.child_count() {
13855        let Some(child) = alternative.child(index) else {
13856            continue;
13857        };
13858        if same_node(child, template_node) {
13859            saw_template = true;
13860            continue;
13861        }
13862        if !saw_template {
13863            continue;
13864        }
13865        if displaced_fragmented_class_terminator(alternative, index) {
13866            terminator = alternative.child(index + 1);
13867            break;
13868        }
13869        if child.is_named() {
13870            member_siblings.push(child);
13871        }
13872    }
13873    let terminator = terminator?;
13874    Some(RecoveredFragmentedPreprocessorClass {
13875        declaration_node,
13876        class_node,
13877        body,
13878        name,
13879        range: Range {
13880            start_byte: class_node.start_byte(),
13881            end_byte: terminator.end_byte(),
13882            start_line: class_node.start_position().row + 1,
13883            end_line: terminator.end_position().row + 1,
13884        },
13885        tail_members,
13886        member_siblings,
13887    })
13888}
13889
13890fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
13891    (0..class_node.child_count()).any(|index| {
13892        class_node.child(index).is_some_and(|child| {
13893            child.kind() == "ERROR"
13894                && (0..child.child_count()).any(|error_index| {
13895                    child
13896                        .child(error_index)
13897                        .is_some_and(|token| token.kind() == "#endif")
13898                })
13899        })
13900    })
13901}
13902
13903/// The real `#endif` that tree-sitter consumed inside an error subtree.
13904///
13905/// A preprocessor directive inside a malformed array bound can cause later
13906/// declarations to remain children of the conditional. The non-missing token
13907/// still gives the exact structured boundary. Ignore nested conditionals and
13908/// select the last unpaired error-owned token. Tree-sitter can pair a later
13909/// outer `#endif` with this conditional, so the direct terminator is not
13910/// necessarily missing.
13911pub fn cpp_displaced_preprocessor_terminator<'tree>(
13912    conditional: Node<'tree>,
13913) -> Option<Node<'tree>> {
13914    if !conditional.has_error() {
13915        return None;
13916    }
13917    let has_concrete_direct_terminator = conditional
13918        .child_count()
13919        .checked_sub(1)
13920        .and_then(|index| conditional.child(index))
13921        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
13922    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
13923        // A structured alternative proves that the direct `#endif` closes
13924        // this family. An error-owned terminator inside either branch belongs
13925        // to a damaged nested conditional, not to this one.
13926        return None;
13927    }
13928    // Pair concrete directive tokens while walking the recovered subtree. An
13929    // inner conditional may be represented only by its `#ifndef`/`#endif`
13930    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
13931    // sufficient: an inner `#endif` can otherwise be mistaken for this
13932    // conditional's displaced terminator.
13933    let mut displaced = None;
13934    let mut conditional_depth = 0usize;
13935    let mut stack = children_iter(conditional)
13936        .map(|child| (child, false))
13937        .collect::<Vec<_>>();
13938    stack.reverse();
13939    while let Some((node, inside_error)) = stack.pop() {
13940        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13941            continue;
13942        }
13943        if node != conditional
13944            && matches!(
13945                node.kind(),
13946                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
13947            )
13948        {
13949            continue;
13950        }
13951        let inside_error = inside_error || node.kind() == "ERROR";
13952        match node.kind() {
13953            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
13954                conditional_depth += 1;
13955            }
13956            "#endif" if !node.is_missing() => {
13957                if conditional_depth == 0
13958                    && inside_error
13959                    && displaced
13960                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
13961                {
13962                    displaced = Some(node);
13963                }
13964                conditional_depth = conditional_depth.saturating_sub(1);
13965            }
13966            _ => {}
13967        }
13968        let first_pushed = stack.len();
13969        stack.extend(children_iter(node).map(|child| (child, inside_error)));
13970        stack[first_pushed..].reverse();
13971    }
13972    displaced
13973}
13974
13975/// The first `#else`/`#elif` of `conditional` that tree-sitter displaced into
13976/// an `ERROR` inside the conditional's own branch.
13977///
13978/// The macro-declaration grammar can read an unterminated end sentinel, the
13979/// branch directive and the next branch's begin sentinel as one declaration
13980/// (`END_NS #else BEGIN_NS class C {};`, #6325): the `#else` becomes an
13981/// `ERROR` child and the conditional loses its `alternative`. The token is
13982/// still the structured branch boundary. Nested conditionals own their own
13983/// directives, including ones represented only by error-owned `#if`/`#endif`
13984/// tokens, so the walk pairs those the same way
13985/// [`cpp_displaced_preprocessor_terminator`] does.
13986pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
13987    if !conditional.has_error() {
13988        return None;
13989    }
13990    let alternative = conditional.child_by_field_name("alternative");
13991    let mut conditional_depth = 0usize;
13992    let mut stack = children_iter(conditional)
13993        .filter(|child| Some(*child) != alternative)
13994        .map(|child| (child, false))
13995        .collect::<Vec<_>>();
13996    stack.reverse();
13997    while let Some((node, inside_error)) = stack.pop() {
13998        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13999            continue;
14000        }
14001        if matches!(
14002            node.kind(),
14003            "preproc_if"
14004                | "preproc_ifdef"
14005                | "preproc_ifndef"
14006                | "preproc_elif"
14007                | "preproc_elifdef"
14008                | "preproc_else"
14009        ) {
14010            continue;
14011        }
14012        let inside_error = inside_error || node.kind() == "ERROR";
14013        match node.kind() {
14014            "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14015            "#endif" if !node.is_missing() => {
14016                conditional_depth = conditional_depth.saturating_sub(1);
14017            }
14018            "#else" | "#elif" | "#elifdef" | "#elifndef"
14019                if inside_error && conditional_depth == 0 && !node.is_missing() =>
14020            {
14021                return Some(node);
14022            }
14023            _ => {}
14024        }
14025        let first_pushed = stack.len();
14026        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14027        stack[first_pushed..].reverse();
14028    }
14029    None
14030}
14031
14032/// The effective end of a conditional whose real terminator tree-sitter
14033/// displaced into declaration recovery.
14034///
14035/// Most damaged conditionals retain a concrete `#endif` token below an
14036/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
14037/// boundary. A preprocessor family that selects the middle of a declaration
14038/// can lose the directive tokens entirely. In that shape tree-sitter leaves
14039/// the declaration's `typedef` token as the sole child of the immediately
14040/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
14041/// declarator name inside the conditional's first declaration. The declaration
14042/// end is then the smallest structured boundary that contains the whole split
14043/// declaration.
14044#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14045pub struct CppDisplacedPreprocessorBoundary {
14046    pub end_byte: usize,
14047    pub end_line: usize,
14048}
14049
14050pub fn cpp_displaced_preprocessor_boundary(
14051    conditional: Node<'_>,
14052) -> Option<CppDisplacedPreprocessorBoundary> {
14053    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14054        return Some(CppDisplacedPreprocessorBoundary {
14055            end_byte: terminator.end_byte(),
14056            end_line: terminator.end_position().row + 1,
14057        });
14058    }
14059    if let Some(declaration) = displaced_split_declaration(conditional) {
14060        return Some(CppDisplacedPreprocessorBoundary {
14061            end_byte: declaration.end_byte(),
14062            end_line: declaration.end_position().row + 1,
14063        });
14064    }
14065    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14066        return Some(CppDisplacedPreprocessorBoundary {
14067            end_byte: terminator.end_byte(),
14068            end_line: terminator.end_position().row + 1,
14069        });
14070    }
14071    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14072        return Some(CppDisplacedPreprocessorBoundary {
14073            end_byte: terminator.end_byte(),
14074            end_line: terminator.end_position().row + 1,
14075        });
14076    }
14077    None
14078}
14079
14080/// Recover an outer terminator that tree-sitter assigned to a damaged nested
14081/// conditional. This occurs when a split construct such as `extern "C"`
14082/// consumes the nested `#endif` inside an error node: the nested conditional's
14083/// direct terminator is then the outer conditional's real terminator, while
14084/// the outer node ends with a missing token and absorbs later declarations.
14085fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14086    if !conditional.has_error()
14087        || conditional.child_by_field_name("alternative").is_some()
14088        || conditional
14089            .child(conditional.child_count().saturating_sub(1))
14090            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14091    {
14092        return None;
14093    }
14094    let mut recovered = None;
14095    for nested in named_children_iter(conditional) {
14096        if !matches!(
14097            nested.kind(),
14098            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14099        ) || nested.child_by_field_name("alternative").is_some()
14100        {
14101            continue;
14102        }
14103        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14104            continue;
14105        };
14106        if direct.kind() != "#endif" || direct.is_missing() {
14107            continue;
14108        }
14109        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14110            continue;
14111        };
14112        if displaced.end_byte() >= direct.start_byte() {
14113            continue;
14114        }
14115        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14116            recovered = Some(direct);
14117        }
14118    }
14119    recovered
14120}
14121
14122fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14123    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14124        return None;
14125    }
14126    let mut cursor = conditional.walk();
14127    let declarations = conditional
14128        .named_children(&mut cursor)
14129        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14130        .collect::<Vec<_>>();
14131    let declaration = *declarations.first()?;
14132    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14133        return None;
14134    }
14135    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14136    let mut terminator = None;
14137    let mut stack = (0..declaration.child_count())
14138        .filter_map(|index| declaration.child(index))
14139        .filter(|child| child.start_byte() < declarator_start)
14140        .map(|child| (child, false))
14141        .collect::<Vec<_>>();
14142    while let Some((node, inside_error)) = stack.pop() {
14143        let inside_error = inside_error || node.kind() == "ERROR";
14144        if inside_error && node.kind() == "#endif" && !node.is_missing() {
14145            terminator = Some(node);
14146            continue;
14147        }
14148        for index in 0..node.child_count() {
14149            if let Some(child) = node.child(index)
14150                && child.start_byte() < declarator_start
14151            {
14152                stack.push((child, inside_error));
14153            }
14154        }
14155    }
14156    terminator
14157}
14158
14159fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14160    if !conditional.has_error()
14161        || conditional.child_by_field_name("alternative").is_some()
14162        || conditional
14163            .prev_named_sibling()
14164            .filter(|sibling| {
14165                sibling.kind() == "ERROR"
14166                    && sibling.child_count() == 1
14167                    && sibling
14168                        .child(0)
14169                        .is_some_and(|child| child.kind() == "typedef")
14170            })
14171            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14172            .is_none()
14173    {
14174        return None;
14175    }
14176    let mut cursor = conditional.walk();
14177    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14178    let declaration_index = children
14179        .iter()
14180        .position(|child| child.kind() == "declaration" && child.has_error())?;
14181    let declaration = children[declaration_index];
14182    if !children
14183        .iter()
14184        .skip(declaration_index + 1)
14185        .any(|child| child.end_byte() > declaration.end_byte())
14186    {
14187        return None;
14188    }
14189    let declarator = declaration.child_by_field_name("declarator")?;
14190    let mut error_end = None;
14191    let mut names = Vec::new();
14192    let mut stack = vec![declarator];
14193    while let Some(node) = stack.pop() {
14194        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14195            error_end =
14196                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14197            continue;
14198        }
14199        if matches!(node.kind(), "identifier" | "type_identifier") {
14200            names.push(node.start_byte());
14201        }
14202        push_named_children_reversed(node, &mut stack);
14203    }
14204    let error_end = error_end?;
14205    names
14206        .into_iter()
14207        .any(|start| start >= error_end)
14208        .then_some(declaration)
14209}
14210
14211fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14212    let Some(error) = parent.child(error_index) else {
14213        return false;
14214    };
14215    if error.kind() != "ERROR"
14216        || error.child_count() != 1
14217        || error.child(0).is_none_or(|child| child.kind() != "}")
14218    {
14219        return false;
14220    }
14221    let Some(semicolon) = parent.child(error_index + 1) else {
14222        return false;
14223    };
14224    semicolon.kind() == "expression_statement"
14225        && semicolon.child_count() == 1
14226        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14227}
14228
14229/// Locate the real end of a class-like declaration when a macro invocation
14230/// without a source semicolon absorbs the class's `};` into its parsed field.
14231/// The grammar then keeps following namespace declarations as later children
14232/// of the same field list. The direct ERROR-plus-semicolon pair proves the
14233/// boundary structurally; no source-text delimiter scan is needed.
14234fn displaced_macro_class_tail(
14235    declaration_node: Node<'_>,
14236    body: Node<'_>,
14237    source: &str,
14238) -> Option<DisplacedMacroClassTail> {
14239    if !matches!(
14240        declaration_node.kind(),
14241        "class_specifier" | "struct_specifier" | "union_specifier"
14242    ) || body.kind() != "field_declaration_list"
14243    {
14244        return None;
14245    }
14246
14247    let child_count = body.named_child_count();
14248    for index in 0..child_count {
14249        let child = body.named_child(index)?;
14250        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14251            continue;
14252        };
14253        let split_index = index + 1;
14254        if split_index >= child_count {
14255            return None;
14256        }
14257        let mut cursor = body.walk();
14258        if !body
14259            .named_children(&mut cursor)
14260            .skip(split_index)
14261            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14262        {
14263            return None;
14264        }
14265        return Some(DisplacedMacroClassTail {
14266            split_index,
14267            class_range: Range {
14268                start_byte: declaration_node.start_byte(),
14269                end_byte: terminator.end_byte(),
14270                start_line: declaration_node.start_position().row + 1,
14271                end_line: terminator.end_position().row + 1,
14272            },
14273        });
14274    }
14275    None
14276}
14277
14278fn displaced_macro_field_terminator<'tree>(
14279    field: Node<'tree>,
14280    source: &str,
14281) -> Option<Node<'tree>> {
14282    if field.kind() != "field_declaration" {
14283        return None;
14284    }
14285    // The older grammar reads the unterminated `MACRO(arg)` invocation as a
14286    // macro-token type and a `parenthesized_declarator`; the macro-declaration
14287    // grammar reads the whole invocation as a `macro_type_specifier`.
14288    let macro_type = field.child_by_field_name("type")?;
14289    let macro_name = match macro_type.kind() {
14290        "type_identifier"
14291            if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14292        {
14293            macro_type
14294        }
14295        "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14296        _ => return None,
14297    };
14298    if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14299        return None;
14300    }
14301    for index in 0..field.child_count() {
14302        let error = field.child(index)?;
14303        if error.kind() != "ERROR"
14304            || error.child_count() != 1
14305            || error.child(0).is_none_or(|child| child.kind() != "}")
14306        {
14307            continue;
14308        }
14309        let semicolon = field.child(index + 1)?;
14310        if semicolon.kind() == ";" {
14311            return Some(semicolon);
14312        }
14313    }
14314    None
14315}
14316
14317fn recover_fragmented_partial_specialization<'tree>(
14318    template_node: Node<'tree>,
14319    declaration_child: Node<'tree>,
14320    source: &str,
14321    ancestry: &ParentIndex<'tree>,
14322) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14323    if declaration_child.kind() != "function_definition" {
14324        return None;
14325    }
14326    let class_node = declaration_child.child_by_field_name("type")?;
14327    if !matches!(
14328        class_node.kind(),
14329        "class_specifier" | "struct_specifier" | "union_specifier"
14330    ) || !class_node
14331        .child_by_field_name("name")
14332        .and_then(|name| direct_identifier_name(name, source))
14333        .is_some_and(|name| cpp_export_macro_token(&name))
14334    {
14335        return None;
14336    }
14337    let declarator = declaration_child.child_by_field_name("declarator")?;
14338    if declarator.kind() != "template_function" {
14339        return None;
14340    }
14341    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14342    if metadata.specialization_arguments.is_empty() {
14343        return None;
14344    }
14345    let body = declaration_child.child_by_field_name("body")?;
14346    if body.kind() != "compound_statement" {
14347        return None;
14348    }
14349    let complete_prefix = body.named_child(0).filter(|first| {
14350        first.kind() == "labeled_statement"
14351            && first.has_error()
14352            && first
14353                .named_child(first.named_child_count().saturating_sub(1))
14354                .is_some_and(recovered_declaration_has_class_terminator)
14355    });
14356    let complete_body = complete_prefix.is_some();
14357    let mut prefix_members = Vec::new();
14358    if let Some(prefix) = complete_prefix {
14359        prefix_members.push(prefix);
14360    } else {
14361        let mut body_cursor = body.walk();
14362        for child in body.named_children(&mut body_cursor) {
14363            if !is_structurally_valid_fragmented_class_prefix_member(child) {
14364                break;
14365            }
14366            prefix_members.push(child);
14367        }
14368    }
14369    let containing_declarations = template_node.parent()?;
14370    if !matches!(
14371        containing_declarations.kind(),
14372        "declaration_list" | "compound_statement"
14373    ) {
14374        return None;
14375    }
14376    let mut member_siblings = Vec::new();
14377    let mut following_declarations = Vec::new();
14378    let terminator;
14379    if complete_body {
14380        terminator = complete_prefix?;
14381        let mut cursor = body.walk();
14382        let mut after_prefix = false;
14383        for child in body.named_children(&mut cursor) {
14384            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14385                after_prefix = true;
14386            } else if after_prefix {
14387                following_declarations.push(child);
14388            }
14389        }
14390    } else {
14391        let mut found_template = false;
14392        let mut cursor = containing_declarations.walk();
14393        let mut class_terminator = None;
14394        for child in containing_declarations.children(&mut cursor) {
14395            if same_node(child, template_node) {
14396                found_template = true;
14397                continue;
14398            }
14399            if found_template && child.kind() == "}" {
14400                class_terminator = Some(child);
14401                break;
14402            }
14403            // A namespace can never be a class member: reaching one before the
14404            // terminator proves the class's true close was swallowed upstream
14405            // and this scan has crossed into the enclosing scope, so the
14406            // recovery cannot be bounded -- continuing re-owns the namespace
14407            // block (and its template specializations) as class members under
14408            // a re-appended package, desyncing the fq boundary (#2306).
14409            if found_template && child.kind() == "namespace_definition" {
14410                return None;
14411            }
14412            if found_template && child.is_named() {
14413                member_siblings.push(child);
14414            }
14415        }
14416        terminator = class_terminator?;
14417    }
14418    let name = format!(
14419        "{}<{}>",
14420        metadata.primary_name,
14421        metadata
14422            .specialization_arguments
14423            .iter()
14424            .map(|argument| argument.text.as_str())
14425            .collect::<Vec<_>>()
14426            .join(", ")
14427    );
14428    Some(RecoveredFragmentedPartialSpecialization {
14429        declaration_node: declaration_child,
14430        name,
14431        range: Range {
14432            start_byte: declaration_child.start_byte(),
14433            end_byte: terminator.end_byte(),
14434            start_line: declaration_child.start_position().row + 1,
14435            end_line: terminator.end_position().row + 1,
14436        },
14437        prefix_members,
14438        member_siblings,
14439        following_declarations,
14440    })
14441}
14442
14443/// Whether `node` is the function-shaped parser wrapper whose body was
14444/// recovered as a fragmented partial-specialization class body.
14445///
14446/// Consumers that walk lexical function scopes must use the same complete
14447/// structural proof as declaration extraction. Otherwise member callables in
14448/// the recovered body are mistaken for function-local declarations.
14449pub fn is_recovered_fragmented_partial_specialization_container(
14450    node: Node<'_>,
14451    source: &str,
14452) -> bool {
14453    let Some(template) = node
14454        .parent()
14455        .filter(|parent| parent.kind() == "template_declaration")
14456    else {
14457        return false;
14458    };
14459    let mut root = template;
14460    while let Some(parent) = root.parent() {
14461        root = parent;
14462    }
14463    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14464        .is_some()
14465}
14466
14467fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14468    if declaration.kind() != "declaration" {
14469        return false;
14470    }
14471    // With an export macro between `class` and its name, tree-sitter folds a
14472    // complete class body into a function-shaped declaration. The class's own
14473    // `};` remains structurally identifiable as a direct ERROR child holding
14474    // `}`, immediately followed by the declaration's direct `;` child.
14475    (0..declaration.child_count().saturating_sub(1)).any(|index| {
14476        let Some(error) = declaration.child(index) else {
14477            return false;
14478        };
14479        error.kind() == "ERROR"
14480            && error.child_count() == 1
14481            && error.child(0).is_some_and(|child| child.kind() == "}")
14482            && declaration
14483                .child(index + 1)
14484                .is_some_and(|child| child.kind() == ";")
14485    })
14486}
14487
14488fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14489    if node.has_error() {
14490        return false;
14491    }
14492    match node.kind() {
14493        "declaration"
14494        | "field_declaration"
14495        | "alias_declaration"
14496        | "type_definition"
14497        | "static_assert_declaration" => true,
14498        "labeled_statement" => node
14499            .named_child(node.named_child_count().saturating_sub(1))
14500            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14501        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14502            matches!(
14503                child.kind(),
14504                "declaration"
14505                    | "field_declaration"
14506                    | "alias_declaration"
14507                    | "type_definition"
14508                    | "function_definition"
14509            )
14510        }),
14511        _ => false,
14512    }
14513}
14514
14515fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14516    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14517        .then(|| node.child_by_field_name("declarator"))
14518        .flatten()
14519        .and_then(|declarator| extract_variable_name(declarator, source))
14520}
14521
14522use crate::graph::syntax::has_function_scope_ancestor;
14523
14524fn cpp_template_metadata<'tree>(
14525    template_node: Node<'tree>,
14526    declaration_child: Node<'tree>,
14527    source: &str,
14528    ancestry: &ParentIndex<'tree>,
14529) -> Option<CppTemplateMetadata> {
14530    let parameters_node = template_node.child_by_field_name("parameters")?;
14531    let name_node = cpp_templated_class_name_node(declaration_child)?;
14532    let primary_node = match name_node.kind() {
14533        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14534        _ => name_node,
14535    };
14536    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14537    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14538        return None;
14539    }
14540
14541    let mut parameter_nodes = Vec::new();
14542    let mut parameter_names = Vec::new();
14543    let mut cursor = parameters_node.walk();
14544    for parameter in parameters_node.named_children(&mut cursor) {
14545        if !matches!(
14546            parameter.kind(),
14547            "type_parameter_declaration"
14548                | "optional_type_parameter_declaration"
14549                | "variadic_type_parameter_declaration"
14550                | "template_template_parameter_declaration"
14551                | "parameter_declaration"
14552                | "optional_parameter_declaration"
14553                | "variadic_parameter_declaration"
14554        ) {
14555            continue;
14556        }
14557        let index = parameter_nodes.len();
14558        // An unnamed parameter still contributes template arity and kind. Use
14559        // an impossible C++ identifier so positional reconciliation can bind
14560        // it without making source expressions refer to a name that was not
14561        // written.
14562        let name = cpp_template_parameter_name(parameter, source)
14563            .unwrap_or_else(|| format!("<anonymous:{index}>"));
14564        parameter_names.push(name);
14565        parameter_nodes.push(parameter);
14566    }
14567    let parameters = parameter_nodes
14568        .into_iter()
14569        .zip(parameter_names.iter().cloned())
14570        .map(|(parameter, name)| CppTemplateParameterMetadata {
14571            name,
14572            kind: cpp_template_parameter_kind(parameter),
14573            variadic: matches!(
14574                parameter.kind(),
14575                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14576            ),
14577            default: cpp_template_parameter_default_expression(
14578                parameter,
14579                source,
14580                &parameter_names,
14581                ancestry,
14582            ),
14583        })
14584        .collect();
14585    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14586        Vec::new()
14587    } else {
14588        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
14589            .unwrap_or_default()
14590    };
14591    let alias_target = (declaration_child.kind() == "alias_declaration")
14592        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
14593        .flatten();
14594    Some(CppTemplateMetadata {
14595        primary_name,
14596        primary_fq_name: String::new(),
14597        parameters,
14598        specialization_arguments,
14599        alias_target,
14600    })
14601}
14602
14603fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14604    match node.kind() {
14605        "class_specifier" | "struct_specifier" | "union_specifier" => {
14606            node.child_by_field_name("name")
14607        }
14608        "function_definition" => {
14609            let declarator = node.child_by_field_name("declarator")?;
14610            if matches!(declarator.kind(), "identifier" | "template_function") {
14611                Some(declarator)
14612            } else {
14613                None
14614            }
14615        }
14616        "alias_declaration" => node.child_by_field_name("name"),
14617        _ => None,
14618    }
14619}
14620
14621fn cpp_template_alias_target<'tree>(
14622    alias: Node<'tree>,
14623    source: &str,
14624    parameter_names: &[String],
14625    ancestry: &ParentIndex<'tree>,
14626) -> Option<CppTemplateAliasTargetMetadata> {
14627    let mut type_node = alias.child_by_field_name("type")?;
14628    while type_node.kind() == "type_descriptor" {
14629        type_node = type_node.child_by_field_name("type")?;
14630    }
14631    let global = type_node.child_by_field_name("scope").is_none()
14632        && type_node.child(0).is_some_and(|child| child.kind() == "::");
14633    let mut components = Vec::new();
14634    cpp_template_target_components(type_node, source, &mut components)?;
14635    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14636    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14637        components,
14638        global,
14639        arguments,
14640    })
14641}
14642
14643fn cpp_template_target_components(
14644    node: Node<'_>,
14645    source: &str,
14646    out: &mut Vec<String>,
14647) -> Option<()> {
14648    // Delay reading the terminal name until the scope has been processed,
14649    // preserving the partial output even when a later component is missing.
14650    enum Component<'tree> {
14651        Node(Node<'tree>),
14652        Name(Node<'tree>),
14653    }
14654    let mut stack = vec![Component::Node(node)];
14655    while let Some(component) = stack.pop() {
14656        let node = match component {
14657            Component::Node(node) => node,
14658            Component::Name(node) => node.child_by_field_name("name")?,
14659        };
14660        match node.kind() {
14661            "identifier" | "namespace_identifier" | "type_identifier" => {
14662                out.push(node_text(node, source).to_string());
14663            }
14664            "template_type" => stack.push(Component::Name(node)),
14665            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14666                stack.push(Component::Name(node));
14667                if let Some(scope) = node.child_by_field_name("scope") {
14668                    stack.push(Component::Node(scope));
14669                }
14670            }
14671            _ => return None,
14672        }
14673    }
14674    Some(())
14675}
14676
14677fn cpp_template_argument_expressions<'tree>(
14678    mut node: Node<'tree>,
14679    source: &str,
14680    parameter_names: &[String],
14681    ancestry: &ParentIndex<'tree>,
14682) -> Option<Vec<CppTemplateExpression>> {
14683    loop {
14684        match node.kind() {
14685            "template_type" | "template_function" => {
14686                let arguments = node.child_by_field_name("arguments")?;
14687                let mut cursor = arguments.walk();
14688                return Some(
14689                    arguments
14690                        .named_children(&mut cursor)
14691                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14692                        .map(|argument| {
14693                            cpp_template_expression(argument, source, parameter_names, ancestry)
14694                        })
14695                        .collect(),
14696                );
14697            }
14698            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14699                node = node
14700                    .child_by_field_name("name")
14701                    .or_else(|| node.child_by_field_name("type"))?;
14702            }
14703            _ => return None,
14704        }
14705    }
14706}
14707
14708fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14709    if node.kind() == "template_template_parameter_declaration" {
14710        let mut cursor = node.walk();
14711        return node
14712            .named_children(&mut cursor)
14713            .find(|child| {
14714                matches!(
14715                    child.kind(),
14716                    "type_parameter_declaration"
14717                        | "optional_type_parameter_declaration"
14718                        | "variadic_type_parameter_declaration"
14719                )
14720            })
14721            .expect("a template-template parameter has a type parameter declaration");
14722    }
14723    node
14724}
14725
14726pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14727    let node = cpp_template_parameter_declaration(node);
14728    let candidate = node
14729        .child_by_field_name("name")
14730        .or_else(|| node.child_by_field_name("declarator"))
14731        .or_else(|| cpp_recovered_type_parameter_name(node))
14732        .or_else(|| {
14733            let mut cursor = node.walk();
14734            node.named_children(&mut cursor).find(|child| {
14735                matches!(
14736                    child.kind(),
14737                    "identifier" | "type_identifier" | "field_identifier"
14738                )
14739            })
14740        })?;
14741    if let Some(name) = direct_identifier_name(candidate, source) {
14742        return Some(name);
14743    }
14744    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14745    (!name.is_empty()).then_some(name)
14746}
14747
14748pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14749    match node.kind() {
14750        "type_parameter_declaration"
14751        | "optional_type_parameter_declaration"
14752        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14753        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14754        _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14755        _ => CppTemplateParameterKind::Value,
14756    }
14757}
14758
14759/// The name of a defaulted type parameter that tree-sitter-cpp recovered as a
14760/// non-type parameter. The grammar's `default_type` cannot hold an abstract
14761/// declarator, so `typename U = T*` always recovers the `*` as an ERROR
14762/// sibling; since grammar 0.24.4 the parameter itself also becomes an
14763/// `optional_parameter_declaration` with `type: dependent_type(typename U)`,
14764/// no declarator and `default_value: T`. A valid dependent type names a
14765/// qualified type after `typename`, so a bare type identifier there is the
14766/// type parameter's name.
14767pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14768    if parameter.kind() != "optional_parameter_declaration"
14769        || parameter.child_by_field_name("declarator").is_some()
14770    {
14771        return None;
14772    }
14773    let dependent = parameter
14774        .child_by_field_name("type")
14775        .filter(|type_node| type_node.kind() == "dependent_type")?;
14776    let mut cursor = dependent.walk();
14777    let mut named = dependent
14778        .named_children(&mut cursor)
14779        .filter(|child| !child.is_extra());
14780    match (named.next(), named.next()) {
14781        (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14782        _ => None,
14783    }
14784}
14785
14786#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14787pub struct CppTemplateParameterNameMatches {
14788    pub any_parameter: bool,
14789    pub type_parameter: bool,
14790}
14791
14792pub fn cpp_template_parameter_name_matches(
14793    parameters: Node<'_>,
14794    name: &str,
14795    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14796) -> CppTemplateParameterNameMatches {
14797    let mut matches = CppTemplateParameterNameMatches::default();
14798    let mut cursor = parameters.walk();
14799    for parameter in parameters.named_children(&mut cursor) {
14800        if parameter_name(parameter).as_deref() == Some(name) {
14801            matches.any_parameter = true;
14802            matches.type_parameter |=
14803                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14804        }
14805    }
14806    matches
14807}
14808
14809pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14810    parent.child_by_field_name("type").is_some_and(|type_node| {
14811        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14812    })
14813}
14814
14815fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14816    let node = cpp_template_parameter_declaration(node);
14817    node.child_by_field_name("default_type")
14818        .or_else(|| node.child_by_field_name("default_value"))
14819}
14820
14821fn cpp_template_parameter_default_expression<'tree>(
14822    parameter: Node<'tree>,
14823    source: &str,
14824    parameter_names: &[String],
14825    ancestry: &ParentIndex<'tree>,
14826) -> Option<CppTemplateExpression> {
14827    let default = cpp_template_parameter_default(parameter)?;
14828    let base = cpp_template_expression(default, source, parameter_names, ancestry);
14829    let Some(pointer_error) = parameter.next_named_sibling() else {
14830        return Some(base);
14831    };
14832    // The grammar's default_type accepts a type specifier, not a complete
14833    // type-id. For `const T*` it calls `const` the type and leaves `T*` in
14834    // the next ERROR. Let the type-id grammar interpret that exact range.
14835    if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14836        && pointer_error.is_error()
14837    {
14838        let text = &source[default.start_byte()..pointer_error.end_byte()];
14839        let parsed = crate::graph::syntax::parse_sentinel(
14840            "using __bifrost_default = ",
14841            crate::graph::syntax::SentinelInput::region(text),
14842            ";",
14843        );
14844        let root = parsed.tree.root_node();
14845        if !root.has_error()
14846            && let Some(type_node) =
14847                brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14848                    root,
14849                    |child| !child.is_extra(),
14850                )
14851                .filter(|alias| alias.kind() == "alias_declaration")
14852                .and_then(|alias| alias.child_by_field_name("type"))
14853        {
14854            let alias_ancestry = ParentIndex::new(root);
14855            return Some(cpp_template_expression(
14856                type_node,
14857                &parsed.source,
14858                parameter_names,
14859                &alias_ancestry,
14860            ));
14861        }
14862    }
14863    let Some(pointer_declarator) =
14864        recovered_abstract_pointer_declarator_term(pointer_error, source)
14865    else {
14866        return Some(base);
14867    };
14868    Some(CppTemplateExpression {
14869        text: format!(
14870            "{}{}",
14871            base.text,
14872            normalize_cpp_whitespace(node_text(pointer_error, source))
14873        ),
14874        term: CppTemplateTerm::Node {
14875            kind: "type_descriptor".to_string(),
14876            children: vec![base.term, pointer_declarator],
14877        },
14878    })
14879}
14880
14881fn recovered_abstract_pointer_declarator_term(
14882    node: Node<'_>,
14883    source: &str,
14884) -> Option<CppTemplateTerm> {
14885    if node.kind() != "ERROR" || node.child_count() == 0 {
14886        return None;
14887    }
14888    let mut children = Vec::new();
14889    for index in 0..node.child_count() {
14890        let child = node.child(index)?;
14891        if child.kind() != "*" {
14892            return None;
14893        }
14894        children.push(CppTemplateTerm::Atom {
14895            kind: "*".to_string(),
14896            text: normalize_cpp_whitespace(node_text(child, source)),
14897        });
14898    }
14899    Some(CppTemplateTerm::Node {
14900        kind: "abstract_pointer_declarator".to_string(),
14901        children,
14902    })
14903}
14904
14905fn cpp_template_expression<'tree>(
14906    node: Node<'tree>,
14907    source: &str,
14908    parameter_names: &[String],
14909    ancestry: &ParentIndex<'tree>,
14910) -> CppTemplateExpression {
14911    let text = normalize_cpp_whitespace(node_text(node, source));
14912    CppTemplateExpression {
14913        text,
14914        term: cpp_template_term(node, source, parameter_names, ancestry),
14915    }
14916}
14917
14918pub fn cpp_template_term<'tree>(
14919    node: Node<'tree>,
14920    source: &str,
14921    parameter_names: &[String],
14922    ancestry: &ParentIndex<'tree>,
14923) -> CppTemplateTerm {
14924    enum Work<'tree> {
14925        Visit(Node<'tree>),
14926        Build { kind: String, child_count: usize },
14927    }
14928
14929    let mut work = vec![Work::Visit(node)];
14930    let mut terms = Vec::new();
14931    while let Some(next) = work.pop() {
14932        match next {
14933            Work::Visit(current) => {
14934                let text = normalize_cpp_whitespace(node_text(current, source));
14935                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
14936                    terms.push(CppTemplateTerm::Parameter(text));
14937                    continue;
14938                }
14939                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
14940                    let mut cursor = current.walk();
14941                    let named = current
14942                        .named_children(&mut cursor)
14943                        .filter(|child| !child.is_extra() && child.kind() != "comment")
14944                        .collect::<Vec<_>>();
14945                    if let [child] = named.as_slice() {
14946                        work.push(Work::Visit(*child));
14947                        continue;
14948                    }
14949                }
14950                if current.child_count() == 0 {
14951                    terms.push(CppTemplateTerm::Atom {
14952                        kind: if matches!(
14953                            current.kind(),
14954                            "identifier"
14955                                | "type_identifier"
14956                                | "field_identifier"
14957                                | "namespace_identifier"
14958                        ) {
14959                            "identifier".to_string()
14960                        } else {
14961                            current.kind().to_string()
14962                        },
14963                        text,
14964                    });
14965                    continue;
14966                }
14967                let children = (0..current.child_count())
14968                    .filter_map(|index| current.child(index))
14969                    .filter(|child| !child.is_extra() && child.kind() != "comment")
14970                    .collect::<Vec<_>>();
14971                work.push(Work::Build {
14972                    kind: current.kind().to_string(),
14973                    child_count: children.len(),
14974                });
14975                work.extend(children.into_iter().rev().map(Work::Visit));
14976            }
14977            Work::Build { kind, child_count } => {
14978                let children = terms.split_off(terms.len() - child_count);
14979                terms.push(CppTemplateTerm::Node { kind, children });
14980            }
14981        }
14982    }
14983    terms.pop().expect("template term traversal emits one root")
14984}
14985
14986fn cpp_template_term_leaf_is_parameter<'tree>(
14987    node: Node<'tree>,
14988    text: &str,
14989    parameter_names: &[String],
14990    ancestry: &ParentIndex<'tree>,
14991) -> bool {
14992    if !parameter_names.iter().any(|parameter| parameter == text) {
14993        return false;
14994    }
14995    !ancestry.parent(node).is_some_and(|parent| {
14996        matches!(
14997            parent.kind(),
14998            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
14999        ) && parent.child_by_field_name("scope").is_some()
15000            && parent.child_by_field_name("name") == Some(node)
15001    })
15002}
15003
15004fn enclosing_cpp_declaration_node<'tree>(
15005    mut node: Node<'tree>,
15006    ancestry: &ParentIndex<'tree>,
15007) -> Option<Node<'tree>> {
15008    loop {
15009        match node.kind() {
15010            "declaration"
15011            | "function_declaration"
15012            | "field_declaration"
15013            | "function_definition" => return Some(node),
15014            _ => node = ancestry.parent(node)?,
15015        }
15016    }
15017}
15018
15019fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15020    let mut params = Vec::new();
15021    let mut cursor = parameters_node.walk();
15022    for child in parameters_node.children(&mut cursor) {
15023        match child.kind() {
15024            "explicit_object_parameter_declaration" => {
15025                if let Some(parameter) = child
15026                    .named_children(&mut child.walk())
15027                    .find(|node| node.kind() == "parameter_declaration")
15028                {
15029                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15030                }
15031            }
15032            "parameter_declaration" | "optional_parameter_declaration" => {
15033                params.push(cpp_parameter_type(child, source));
15034            }
15035            "variadic_parameter_declaration" => {
15036                params.push(cpp_parameter_type(child, source));
15037            }
15038            "variadic_parameter" | "..." => params.push("...".to_string()),
15039            _ => {}
15040        }
15041    }
15042
15043    if params.is_empty() {
15044        "()".to_string()
15045    } else {
15046        format!("({})", params.join(", "))
15047    }
15048}
15049
15050/// Where a C or C++ callable declaration is written, which is what decides
15051/// which modifier facts its own node is able to state.
15052enum CppCallableScope<'tree> {
15053    /// Written directly in a class, struct or union body. The declaration
15054    /// states its own storage class, and its access is the one the body's
15055    /// specifier ladder is under at that point.
15056    ClassBody {
15057        body: Node<'tree>,
15058        default_visibility: DeclaredVisibility,
15059    },
15060    /// A qualified declaration written outside every class body
15061    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
15062    /// writes no access specifier, and whether the qualifier names a class or
15063    /// a namespace is not decidable from this file alone -- that is exactly
15064    /// what [`crate::reconcile`] needs the include-visible class table for.
15065    Qualified,
15066    /// Namespace scope or file scope, reached by an unqualified name. C has no
15067    /// member functions at all, so every C callable lands here.
15068    Unqualified,
15069}
15070
15071/// Where the declaration sits, from one upward walk of its own ancestry.
15072///
15073/// The walk stops as soon as something proves the declaration is outside every
15074/// class body: a `namespace_definition`, because a class body never encloses
15075/// one, and a `compound_statement`, because a body opens a block scope and
15076/// nothing declared in it is a member of the class the enclosing definition
15077/// belongs to. That is the same rule
15078/// `CppVisitor::visit_anonymous_local_aggregates_in_function` applies when it
15079/// drops the aggregate carrier, and it keeps this question off the O(depth)
15080/// walk a deeply nested generated header would otherwise pay per callable.
15081///
15082/// `function_declarator` is `None` for a declaration whose callable the macro
15083/// recovery reparsed out of a malformed member: there is no declarator node to
15084/// ask whether the name carries a qualifier, and a recovered member is written
15085/// in its class body anyway.
15086fn cpp_callable_scope<'tree>(
15087    declaration: Node<'tree>,
15088    function_declarator: Option<Node<'tree>>,
15089    ancestry: &ParentIndex<'tree>,
15090) -> CppCallableScope<'tree> {
15091    let mut current = Some(declaration);
15092    while let Some(node) = current {
15093        match node.kind() {
15094            "field_declaration_list" => {
15095                let default_visibility =
15096                    ancestry
15097                        .parent(node)
15098                        .map_or(DeclaredVisibility::Unknown, |class_like| {
15099                            // `struct` and `union` members default to public;
15100                            // `class` members default to private.
15101                            match class_like.kind() {
15102                                "struct_specifier" | "union_specifier" => {
15103                                    DeclaredVisibility::Public
15104                                }
15105                                _ => DeclaredVisibility::Private,
15106                            }
15107                        });
15108                return CppCallableScope::ClassBody {
15109                    body: node,
15110                    default_visibility,
15111                };
15112            }
15113            "namespace_definition" | "compound_statement" | "translation_unit" => break,
15114            _ => {}
15115        }
15116        current = ancestry.parent(node);
15117    }
15118    if cpp_callable_declarator_is_qualified(function_declarator) {
15119        CppCallableScope::Qualified
15120    } else {
15121        CppCallableScope::Unqualified
15122    }
15123}
15124
15125/// Whether the declarator's own written name carries a `::` qualifier.
15126///
15127/// A qualified declaration written outside every class body
15128/// (`long Client::send(int) {}`) names a member whose class body this file may
15129/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
15130/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
15131fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15132    function_declarator
15133        .and_then(cpp_function_declarator_name_node)
15134        .is_some_and(|name| name.kind() == "qualified_identifier")
15135}
15136
15137/// The access one class-body member declaration is written under.
15138///
15139/// C++ states member access positionally: an `access_specifier` applies to
15140/// every member written after it until the next one, and the members before
15141/// the first one take the class keyword's default. The answer is therefore the
15142/// last specifier the body writes before the member's own list-level node,
15143/// read from the specifier's own keyword token rather than from its text. A
15144/// label a macro invocation swallowed has no specifier node or keyword token;
15145/// [`cpp_macro_swallowed_access_label`] reads it from the declarator the
15146/// grammar made of the keyword.
15147///
15148/// The body's children are read forward with one cursor rather than backwards
15149/// from the member: `Node::prev_sibling` re-descends from the root for each
15150/// step, which is the per-edge cost #2361 keeps off this walk.
15151fn cpp_member_declared_visibility<'tree>(
15152    declaration: Node<'tree>,
15153    body: Node<'tree>,
15154    default_visibility: DeclaredVisibility,
15155    source: &str,
15156    ancestry: &ParentIndex<'tree>,
15157) -> DeclaredVisibility {
15158    let mut list_level = declaration;
15159    while ancestry.parent(list_level) != Some(body) {
15160        let Some(parent) = ancestry.parent(list_level) else {
15161            return default_visibility;
15162        };
15163        list_level = parent;
15164    }
15165    let mut visibility = default_visibility;
15166    let mut cursor = body.walk();
15167    for child in body.children(&mut cursor) {
15168        if child == list_level {
15169            break;
15170        }
15171        let access = if child.kind() == "access_specifier" {
15172            child.child(0).map(|keyword| keyword.kind())
15173        } else {
15174            cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15175        };
15176        visibility = match access {
15177            Some("public") => DeclaredVisibility::Public,
15178            Some("protected") => DeclaredVisibility::Protected,
15179            Some("private") => DeclaredVisibility::Private,
15180            _ => visibility,
15181        };
15182    }
15183    visibility
15184}
15185
15186/// Whether this declaration writes the `static` storage class specifier.
15187///
15188/// What `static` *means* is the scope's business: on a class-body member it is
15189/// the receiver fact, and at file scope it is internal linkage, which is why
15190/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
15191/// question and answer it differently.
15192fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15193    let mut cursor = declaration.walk();
15194    declaration.named_children(&mut cursor).any(|child| {
15195        child.kind() == "storage_class_specifier"
15196            && normalize_cpp_whitespace(node_text(child, source)) == "static"
15197    })
15198}
15199
15200/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
15201/// publishes, as C and C++ state them.
15202struct CppCallableModifiers {
15203    is_static: bool,
15204    is_constructor: bool,
15205    visibility: DeclaredVisibility,
15206}
15207
15208/// The modifier facts a C or C++ callable declaration states in its own shape.
15209///
15210/// C++ has no `static` keyword for receivers and no visibility keyword of the
15211/// Java kind: it states both in the declaration's position. A member declared
15212/// in a class body binds the object it is selected on unless that declaration
15213/// writes the `static` storage class, and its access is the one the body's
15214/// specifier ladder is under. A function at namespace or file scope binds
15215/// nothing and has no access specifier at all, so its declared visibility is
15216/// Unknown. Linkage is recorded separately and does not declare access. C has
15217/// no member functions, and the `static` a C function writes is internal linkage,
15218/// never a receiver fact.
15219///
15220/// Recording these is what lets `receiver_contract_of` answer for a C or C++
15221/// declaration. Without them it reported no contract at all,
15222/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
15223/// callable, no reviewed summary could name one, and every effect or taint walk
15224/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
15225/// in #3455, Kotlin in #3453, Python in #3451.
15226///
15227/// An out-of-line member definition cannot repeat `static` or access. These
15228/// are per-file facts only: the analyzer reconciles the exact overload with
15229/// include-visible class declarations before exposing its receiver contract
15230/// (#3509). A missing declaration leaves that contract unknown.
15231fn cpp_callable_modifiers<'tree>(
15232    declaration: Node<'tree>,
15233    function_declarator: Option<Node<'tree>>,
15234    is_constructor: bool,
15235    source: &str,
15236    ancestry: &ParentIndex<'tree>,
15237) -> CppCallableModifiers {
15238    match cpp_callable_scope(declaration, function_declarator, ancestry) {
15239        CppCallableScope::ClassBody {
15240            body,
15241            default_visibility,
15242        } => CppCallableModifiers {
15243            is_static: cpp_declares_static_storage_class(declaration, source),
15244            is_constructor,
15245            visibility: cpp_member_declared_visibility(
15246                declaration,
15247                body,
15248                default_visibility,
15249                source,
15250                ancestry,
15251            ),
15252        },
15253        CppCallableScope::Qualified => CppCallableModifiers {
15254            is_static: false,
15255            is_constructor,
15256            visibility: DeclaredVisibility::Unknown,
15257        },
15258        CppCallableScope::Unqualified => CppCallableModifiers {
15259            is_static: false,
15260            is_constructor,
15261            visibility: DeclaredVisibility::Unknown,
15262        },
15263    }
15264}
15265
15266/// The persisted signature facts one C or C++ callable declaration publishes.
15267///
15268/// `linkage` is the caller's, not this function's: the node that carries the
15269/// storage class is not always the declaration this declarator sits in -- a
15270/// recovered constructor reads it from its class declaration, and a
15271/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
15272/// visit site that knows which node states it passes the answer in. Recording
15273/// it here keeps it beside the declared visibility it also decides.
15274fn cpp_signature_metadata<'tree>(
15275    signature: String,
15276    function_declarator: Node<'tree>,
15277    linkage: CallableLinkage,
15278    source: &str,
15279    ancestry: &ParentIndex<'tree>,
15280) -> SignatureMetadata {
15281    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15282    let is_constructor =
15283        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15284    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15285        Some(declaration) => cpp_callable_modifiers(
15286            declaration,
15287            Some(function_declarator),
15288            is_constructor,
15289            source,
15290            ancestry,
15291        ),
15292        // Severe recovery left no declaration node above this declarator, so
15293        // nothing is stated here beyond what the callable's own name proves.
15294        None => CppCallableModifiers {
15295            is_static: false,
15296            is_constructor,
15297            visibility: DeclaredVisibility::Unknown,
15298        },
15299    };
15300    let enrich = |mut metadata: SignatureMetadata| {
15301        // #3507: a qualified definition repeats none of the class body's
15302        // dispatch facts, so this row publishes none either.
15303        if let Some(dispatch) = dispatch {
15304            metadata = metadata.with_dispatch_extensibility(dispatch);
15305        }
15306        metadata
15307            .with_callable_linkage(linkage)
15308            .with_callable_modifiers(
15309                modifiers.is_static,
15310                modifiers.is_constructor,
15311                modifiers.visibility,
15312            )
15313    };
15314    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15315    let return_type_identity =
15316        cpp_callable_return_type_identity(function_declarator, source, ancestry);
15317    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15318        return enrich(
15319            SignatureMetadata::new(signature, Vec::new())
15320                .with_return_type_text(return_type_text)
15321                .with_return_type_identity(return_type_identity),
15322        );
15323    };
15324    let callable_arity = cpp_callable_arity(parameters_node, source);
15325    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15326    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15327    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15328    let Some(relative_start) = signature
15329        .get(search_from..)
15330        .and_then(|suffix| suffix.find(&parameter_text))
15331    else {
15332        return enrich(
15333            SignatureMetadata::new(signature, Vec::new())
15334                .with_callable_arity(callable_arity)
15335                .with_callable_parameter_types(callable_parameter_types)
15336                .with_return_type_text(return_type_text)
15337                .with_return_type_identity(return_type_identity),
15338        );
15339    };
15340    let parameters_start = search_from + relative_start;
15341    let parameters_end = parameters_start + parameter_text.len();
15342    let mut search_start = parameters_start;
15343    let parameters = cpp_parameter_label_nodes(parameters_node)
15344        .into_iter()
15345        .filter_map(|label_node| {
15346            let label = normalize_cpp_whitespace(node_text(label_node, source));
15347            if label.is_empty() || search_start > parameters_end {
15348                return None;
15349            }
15350            let haystack = signature.get(search_start..parameters_end)?;
15351            let relative_start = haystack.find(&label)?;
15352            let start_byte = search_start + relative_start;
15353            let end_byte = start_byte + label.len();
15354            search_start = end_byte;
15355            Some(ParameterMetadata::new(label, start_byte, end_byte))
15356        })
15357        .collect();
15358    enrich(
15359        SignatureMetadata::new(signature, parameters)
15360            .with_callable_arity(callable_arity)
15361            .with_callable_parameter_types(callable_parameter_types)
15362            .with_return_type_text(return_type_text)
15363            .with_return_type_identity(return_type_identity),
15364    )
15365}
15366
15367pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15368    function_declarator: Node<'tree>,
15369    source: &str,
15370    ancestry: &ParentIndex<'tree>,
15371) -> bool {
15372    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15373        .or_else(|| function_declarator.child_by_field_name("name"))
15374        .or_else(|| last_named_child(function_declarator))
15375    else {
15376        return false;
15377    };
15378    // An out-of-line definition writes the class in the name itself
15379    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
15380    // terminal component answers without any enclosing class body.
15381    if name_node.kind() == "qualified_identifier" {
15382        let owner = name_node
15383            .child_by_field_name("scope")
15384            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15385        let member = name_node
15386            .child_by_field_name("name")
15387            .and_then(|name| canonical_cpp_qualified_component(name, source));
15388        return match (owner, member) {
15389            (Some(owner), Some(member)) => owner.name == member.name,
15390            _ => false,
15391        };
15392    }
15393    let Some(callable_name) = direct_identifier_name(name_node, source) else {
15394        return false;
15395    };
15396
15397    let mut current = ancestry.parent(function_declarator);
15398    while let Some(ancestor) = current {
15399        let owner_name = match ancestor.kind() {
15400            "class_specifier" | "struct_specifier" | "union_specifier" => {
15401                class_like_name(ancestor, source, ancestry)
15402            }
15403            "ERROR" => malformed_class_error_owner_name(ancestor, source),
15404            _ => None,
15405        };
15406        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15407            return true;
15408        }
15409        current = ancestry.parent(ancestor);
15410    }
15411    false
15412}
15413
15414/// Recover the owner name from the direct grammar shape retained when a later
15415/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
15416/// `ERROR` node:
15417///
15418/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
15419///
15420/// Direct-child checks keep this distinct from an unrelated nested class inside
15421/// a broader error region. The closing brace may be displaced past the error
15422/// node, so the opening body token is the available structural boundary.
15423fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15424    if node.kind() != "ERROR" {
15425        return None;
15426    }
15427    let keyword = node.child(0)?;
15428    if !matches!(keyword.kind(), "class" | "struct" | "union") {
15429        return None;
15430    }
15431    let name_node = node.child(1)?;
15432    let name = direct_identifier_name(name_node, source)?;
15433    let has_body = (2..node.child_count())
15434        .filter_map(|index| node.child(index))
15435        .any(|child| child.kind() == "{");
15436    has_body.then_some(name)
15437}
15438
15439/// Whether one declaration carries a native must-use attribute.
15440///
15441/// Attribute names are read only from structurally selected attribute nodes;
15442/// an identifier with the same spelling elsewhere in the declaration cannot
15443/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
15444/// while standard `nodiscard` is accepted without a namespace prefix.
15445pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15446    // The C++ grammar keeps a prefix attribute on the enclosing declaration
15447    // while the enum unit's occurrence points at its `enum_specifier`. Bring
15448    // that one structural sibling into the same declaration boundary; this
15449    // does not widen the walk to surrounding declarations.
15450    let declaration = declaration
15451        .parent()
15452        .filter(|parent| {
15453            parent.kind() == "declaration"
15454                && parent
15455                    .child_by_field_name("type")
15456                    .is_some_and(|type_node| type_node.id() == declaration.id())
15457        })
15458        .unwrap_or(declaration);
15459    let mut pending = vec![declaration];
15460    while let Some(node) = pending.pop() {
15461        if cpp_attribute_node_is_native_must_use(node, source)
15462            && cpp_attribute_is_attached_to_declaration(node, declaration)
15463        {
15464            return true;
15465        }
15466        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15467        {
15468            continue;
15469        }
15470        push_named_children_reversed(node, &mut pending);
15471    }
15472    false
15473}
15474
15475fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15476    if node.kind() == "attribute" {
15477        return node.child_by_field_name("name").is_some_and(|name| {
15478            let name = node_text(name, source);
15479            let prefix = node
15480                .child_by_field_name("prefix")
15481                .map(|prefix| node_text(prefix, source));
15482            matches!(
15483                (prefix, name),
15484                (None, "nodiscard")
15485                    | (None, "warn_unused_result")
15486                    | (Some("gnu"), "warn_unused_result")
15487            )
15488        });
15489    }
15490    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15491    {
15492        return false;
15493    }
15494    let Some(arguments) = node.named_child(0) else {
15495        return false;
15496    };
15497    arguments.kind() == "argument_list"
15498        && arguments.named_children(&mut arguments.walk()).any(|name| {
15499            name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15500        })
15501}
15502
15503fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15504    let mut current = attribute;
15505    while let Some(parent) = current.parent() {
15506        if parent.id() == declaration.id() {
15507            return true;
15508        }
15509        if matches!(
15510            parent.kind(),
15511            "declaration"
15512                | "field_declaration"
15513                | "function_definition"
15514                | "struct_specifier"
15515                | "class_specifier"
15516                | "union_specifier"
15517                | "enum_specifier"
15518        ) {
15519            return false;
15520        }
15521        current = parent;
15522    }
15523    false
15524}
15525
15526/// Whether export/decorator macros ahead of a callable declaration's `type`
15527/// make that type head decoration rather than a semantic return type. The
15528/// older grammar leaves the macro token in the `type` field and the real type
15529/// in an ERROR sibling; the macro-declaration grammar wraps the same token as a
15530/// leading `macro_modifier`, or the whole invocation as a
15531/// `macro_type_specifier` type. Such a declaration does not carry enough
15532/// structured evidence to persist a return type, as text or as identity.
15533fn cpp_declaration_return_type_is_macro_decorated(
15534    declaration: Node<'_>,
15535    type_node: Node<'_>,
15536    source: &str,
15537) -> bool {
15538    type_node.kind() == "macro_type_specifier"
15539        || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15540        || (cpp_export_macro_token(node_text(type_node, source))
15541            && (0..declaration.named_child_count()).any(|index| {
15542                declaration
15543                    .named_child(index)
15544                    .is_some_and(|child| child.kind() == "ERROR")
15545            }))
15546}
15547
15548/// Whether a `macro_modifier` precedes `type_node` among `declaration`'s
15549/// specifiers: the macro-declaration grammar's clean reading of an unexpanded
15550/// decorator macro ahead of the declared type (`MACRO static inline T f()`).
15551pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15552    declaration: Node<'_>,
15553    type_node: Node<'_>,
15554) -> bool {
15555    let mut head_cursor = declaration.walk();
15556    declaration
15557        .named_children(&mut head_cursor)
15558        .take_while(|child| *child != type_node)
15559        .any(|child| child.kind() == "macro_modifier")
15560}
15561
15562fn cpp_callable_return_type_parts<'tree>(
15563    function_declarator: Node<'tree>,
15564    source: &str,
15565    ancestry: &ParentIndex<'tree>,
15566) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15567    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15568        return None;
15569    }
15570    if let Some((return_type, _)) =
15571        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15572    {
15573        return Some((return_type, Vec::new()));
15574    }
15575    let mut cursor = function_declarator.walk();
15576    if let Some(trailing) = function_declarator
15577        .named_children(&mut cursor)
15578        .find(|child| child.kind() == "trailing_return_type")
15579        && let Some(type_descriptor) = trailing.named_child(0)
15580    {
15581        return Some((type_descriptor, Vec::new()));
15582    }
15583
15584    let mut current = function_declarator;
15585    let mut wrappers = Vec::new();
15586    while let Some(parent) = ancestry.parent(current) {
15587        if matches!(
15588            parent.kind(),
15589            "function_definition" | "declaration" | "field_declaration"
15590        ) {
15591            let type_node = parent.child_by_field_name("type")?;
15592            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15593                return None;
15594            }
15595            return Some((type_node, wrappers));
15596        }
15597        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15598            || (matches!(
15599                parent.kind(),
15600                "pointer_declarator"
15601                    | "reference_declarator"
15602                    | "array_declarator"
15603                    | "parenthesized_declarator"
15604            ) && parent.named_child_count() == 1
15605                && parent.named_child(0) == Some(current));
15606        if !wraps_current_declarator {
15607            return None;
15608        }
15609        match parent.kind() {
15610            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15611            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15612            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15613            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15614            _ => return None,
15615        }
15616        current = parent;
15617    }
15618    None
15619}
15620
15621/// Return the AST node that describes one callable's declared result type.
15622///
15623/// The node is returned only after the same declarator walk that establishes
15624/// the persisted C++ return identity. Consumers can therefore resolve a
15625/// nominal result or inspect its declaration attributes without parsing a
15626/// rendered signature.
15627pub fn cpp_callable_declaration_return_type_node<'tree>(
15628    callable: Node<'tree>,
15629    source: &str,
15630    ancestry: &ParentIndex<'tree>,
15631) -> Option<Node<'tree>> {
15632    let declarator = callable
15633        .child_by_field_name("declarator")
15634        .and_then(extract_function_declarator)?;
15635    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15636}
15637
15638/// Return the structured declared result type of one callable declaration.
15639///
15640/// This accepts the enclosing declaration node so consumers do not need to
15641/// duplicate the declarator-unwrapping rules before asking for the structured
15642/// identity.
15643pub fn cpp_callable_declaration_return_type_identity<'tree>(
15644    callable: Node<'tree>,
15645    source: &str,
15646    ancestry: &ParentIndex<'tree>,
15647) -> Option<StructuredTypeIdentity> {
15648    let declarator = callable
15649        .child_by_field_name("declarator")
15650        .and_then(extract_function_declarator)?;
15651    cpp_callable_return_type_identity(declarator, source, ancestry)
15652}
15653
15654pub(crate) fn cpp_callable_return_type_identity<'tree>(
15655    function_declarator: Node<'tree>,
15656    source: &str,
15657    ancestry: &ParentIndex<'tree>,
15658) -> Option<StructuredTypeIdentity> {
15659    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15660    let (return_type, wrappers) =
15661        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15662    cpp_apply_structured_type_wrappers(
15663        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15664        wrappers,
15665    )
15666}
15667
15668fn cpp_structured_type_identity(
15669    node: Node<'_>,
15670    source: &str,
15671    lexical_scope: &[String],
15672) -> Option<StructuredTypeIdentity> {
15673    enum Work<'tree> {
15674        Visit(Node<'tree>),
15675        Wrap(CppStructuredTypeWrapper),
15676        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15677        BuildGeneric { argument_count: usize },
15678    }
15679
15680    let mut work = vec![Work::Visit(node)];
15681    let mut values = Vec::new();
15682    let mut builder = StructuredTypeIdentityBuilder::default();
15683    while let Some(next) = work.pop() {
15684        match next {
15685            Work::Visit(current) => match current.kind() {
15686                "type_descriptor" => {
15687                    let type_node = current
15688                        .child_by_field_name("type")
15689                        .or_else(|| current.named_child(0))?;
15690                    let mut wrappers = Vec::new();
15691                    let mut cursor = current.walk();
15692                    for child in current.named_children(&mut cursor) {
15693                        if child.id() != type_node.id() {
15694                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15695                        }
15696                    }
15697                    work.push(Work::ApplyWrappers(wrappers));
15698                    work.push(Work::Visit(type_node));
15699                }
15700                "pointer_declarator" | "abstract_pointer_declarator" => {
15701                    let child = current
15702                        .child_by_field_name("declarator")
15703                        .or_else(|| current.named_child(0))?;
15704                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15705                    work.push(Work::Visit(child));
15706                }
15707                "reference_declarator" => {
15708                    let child = current
15709                        .child_by_field_name("declarator")
15710                        .or_else(|| current.named_child(0))?;
15711                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15712                    work.push(Work::Visit(child));
15713                }
15714                "array_declarator" | "abstract_array_declarator" => {
15715                    let child = current
15716                        .child_by_field_name("declarator")
15717                        .or_else(|| current.named_child(0))?;
15718                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15719                    work.push(Work::Visit(child));
15720                }
15721                "template_type" => {
15722                    let name_node = current.child_by_field_name("name")?;
15723                    let arguments = current
15724                        .child_by_field_name("arguments")
15725                        .map(|arguments_node| {
15726                            let mut cursor = arguments_node.walk();
15727                            arguments_node
15728                                .named_children(&mut cursor)
15729                                .filter(|child| !child.is_extra() && child.kind() != "comment")
15730                                .collect::<Vec<_>>()
15731                        })
15732                        .unwrap_or_default();
15733                    work.push(Work::BuildGeneric {
15734                        argument_count: arguments.len(),
15735                    });
15736                    work.extend(arguments.into_iter().rev().map(Work::Visit));
15737                    work.push(Work::Visit(name_node));
15738                }
15739                "qualified_identifier"
15740                | "scoped_identifier"
15741                | "scoped_type_identifier"
15742                | "type_identifier"
15743                | "field_identifier"
15744                | "identifier"
15745                | "namespace_identifier"
15746                | "primitive_type" => {
15747                    values.push(builder.named(cpp_structured_named_type(
15748                        current,
15749                        source,
15750                        lexical_scope,
15751                    )?)?);
15752                }
15753                _ => {
15754                    let child = current.child_by_field_name("type").or_else(|| {
15755                        (current.named_child_count() == 1)
15756                            .then(|| current.named_child(0))
15757                            .flatten()
15758                    })?;
15759                    work.push(Work::Visit(child));
15760                }
15761            },
15762            Work::Wrap(wrapper) => {
15763                let root = values.pop()?;
15764                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15765            }
15766            Work::ApplyWrappers(wrappers) => {
15767                let mut root = values.pop()?;
15768                for wrapper in wrappers.into_iter().rev() {
15769                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15770                }
15771                values.push(root);
15772            }
15773            Work::BuildGeneric { argument_count } => {
15774                let value_count = argument_count.checked_add(1)?;
15775                let start = values.len().checked_sub(value_count)?;
15776                let mut built = values.split_off(start);
15777                let base = built.remove(0);
15778                values.push(builder.generic(base, built)?);
15779            }
15780        }
15781    }
15782    (values.len() == 1)
15783        .then(|| values.pop())
15784        .flatten()
15785        .and_then(|root| builder.finish(root))
15786}
15787
15788fn cpp_structured_named_type(
15789    node: Node<'_>,
15790    source: &str,
15791    lexical_scope: &[String],
15792) -> Option<StructuredTypeName> {
15793    let path = cpp_structured_type_path(node, source)?;
15794    let absolute = node.child_by_field_name("scope").is_none()
15795        && node.child(0).is_some_and(|child| child.kind() == "::");
15796    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15797}
15798
15799#[derive(Clone, Copy)]
15800enum CppStructuredTypeWrapper {
15801    Pointer,
15802    LvalueReference,
15803    RvalueReference,
15804    Array,
15805}
15806
15807fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15808    let mut wrappers = Vec::new();
15809    let mut current = node;
15810    loop {
15811        match current.kind() {
15812            "pointer_declarator" | "abstract_pointer_declarator" => {
15813                wrappers.push(CppStructuredTypeWrapper::Pointer)
15814            }
15815            "reference_declarator" | "abstract_reference_declarator" => {
15816                wrappers.push(cpp_reference_wrapper(current)?);
15817            }
15818            "array_declarator" | "abstract_array_declarator" => {
15819                wrappers.push(CppStructuredTypeWrapper::Array)
15820            }
15821            // An initializer and a grouping parenthesis are not type
15822            // structure. Stopping at one of them dropped every wrapper an
15823            // initialized or parenthesized declarator writes, so
15824            // `std::string &alias = value;` reported the unqualified
15825            // `std::string` while `const std::string &value` as a parameter
15826            // reported the reference it shares.
15827            "init_declarator" | "parenthesized_declarator" => {}
15828            _ => break,
15829        }
15830        let Some(child) = current
15831            .child_by_field_name("declarator")
15832            .or_else(|| current.named_child(0))
15833        else {
15834            break;
15835        };
15836        current = child;
15837    }
15838    Some(wrappers)
15839}
15840
15841fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15842    node.children(&mut node.walk())
15843        .find_map(|child| match child.kind() {
15844            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15845            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15846            _ => None,
15847        })
15848}
15849
15850fn cpp_wrap_structured_type(
15851    identity: StructuredTypeIdentity,
15852    wrapper: CppStructuredTypeWrapper,
15853) -> Option<StructuredTypeIdentity> {
15854    match wrapper {
15855        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15856        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15857        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15858        CppStructuredTypeWrapper::Array => identity.wrap_array(),
15859    }
15860}
15861
15862fn cpp_apply_structured_type_wrappers(
15863    mut identity: StructuredTypeIdentity,
15864    wrappers: Vec<CppStructuredTypeWrapper>,
15865) -> Option<StructuredTypeIdentity> {
15866    for wrapper in wrappers.into_iter().rev() {
15867        identity = cpp_wrap_structured_type(identity, wrapper)?;
15868    }
15869    Some(identity)
15870}
15871
15872fn cpp_wrap_structured_type_node(
15873    builder: &mut StructuredTypeIdentityBuilder,
15874    inner: StructuredTypeNodeId,
15875    wrapper: CppStructuredTypeWrapper,
15876) -> Option<StructuredTypeNodeId> {
15877    match wrapper {
15878        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
15879        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
15880        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
15881        CppStructuredTypeWrapper::Array => builder.array(inner),
15882    }
15883}
15884
15885/// The dot-free name components a qualified C++ name node writes, outermost
15886/// first.
15887///
15888/// The walk reads the grammar's `scope` and `name` fields rather than cutting
15889/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
15890/// shape the grammar does not name yields nothing at all.
15891pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
15892    let mut path = Vec::new();
15893    let mut stack = vec![node];
15894    while let Some(current) = stack.pop() {
15895        match current.kind() {
15896            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
15897                let component = node_text(current, source).to_string();
15898                if component.is_empty() {
15899                    return None;
15900                }
15901                path.push(component);
15902            }
15903            "template_type" | "dependent_type" => {
15904                stack.push(current.child_by_field_name("name")?);
15905            }
15906            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15907                stack.push(current.child_by_field_name("name")?);
15908                if let Some(scope) = current.child_by_field_name("scope") {
15909                    stack.push(scope);
15910                }
15911            }
15912            _ => return None,
15913        }
15914    }
15915    (!path.is_empty()).then_some(path)
15916}
15917
15918fn cpp_callable_lexical_scope<'tree>(
15919    node: Node<'tree>,
15920    source: &str,
15921    ancestry: &ParentIndex<'tree>,
15922) -> Vec<String> {
15923    let mut groups = Vec::new();
15924    let mut current = ancestry.parent(node);
15925    while let Some(parent) = current {
15926        if matches!(
15927            parent.kind(),
15928            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
15929        ) && let Some(name_node) = parent.child_by_field_name("name")
15930            && let Some(components) = cpp_structured_type_path(name_node, source)
15931            && !components.is_empty()
15932        {
15933            groups.push(components);
15934        }
15935        current = ancestry.parent(parent);
15936    }
15937    groups.reverse();
15938    groups.into_iter().flatten().collect()
15939}
15940
15941/// Whether a call on this callable's static target can select another body.
15942///
15943/// C++ says this where the member is *declared*, not where it is defined, and
15944/// that is the whole difficulty of the question: virtual-ness is inherited
15945/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
15946/// `Base::run` is), an out-of-line definition repeats none of it, and whether
15947/// a definition's qualifier names a class or a namespace is a class-table
15948/// question. So a row answers `Closed` only when its own node proves it:
15949///
15950/// * a written `final` is the one specifier that ends the family here, and it
15951///   is the only thing that closes a member of a class that has a base clause;
15952/// * a class-body member is `Closed` otherwise only when its class body writes
15953///   no base clause at all, because a base clause is the only evidence in this
15954///   file that some base may declare this member virtual;
15955/// * anything with a virtual-family boundary, a template head, or a
15956///   preprocessor or error region is `Open`;
15957/// * a qualified declaration outside every class body answers `None`: its
15958///   class body, which is where the fact is written, may be in another file.
15959///
15960/// The analyzer joins the `None` rows with the include-visible class-body rows
15961/// of the same logical callable (#3507), the same join #3509 makes for a
15962/// definition's declared access; a row that keeps `None` after that join stays
15963/// unknown and no dispatch proof may be built on it.
15964fn cpp_callable_dispatch_extensibility<'tree>(
15965    function_declarator: Node<'tree>,
15966    ancestry: &ParentIndex<'tree>,
15967) -> Option<DispatchExtensibility> {
15968    let mut declaration = None;
15969    // The first class body this declarator is written in. The upward walk
15970    // visits it anyway, so only the one step from the body to its class node
15971    // is extra, and only for a class-body member.
15972    let mut class_body = None;
15973    let mut current = Some(function_declarator);
15974    while let Some(node) = current {
15975        match node.kind() {
15976            "template_declaration"
15977            | "preproc_if"
15978            | "preproc_ifdef"
15979            | "preproc_else"
15980            | "preproc_elif"
15981            | "preproc_call"
15982            | "ERROR" => return Some(DispatchExtensibility::Open),
15983            "declaration" | "field_declaration" | "function_definition" => {
15984                declaration.get_or_insert(node);
15985            }
15986            "field_declaration_list" => {
15987                class_body.get_or_insert(node);
15988            }
15989            "translation_unit" => break,
15990            _ => {}
15991        }
15992        current = ancestry.parent(node);
15993    }
15994    let Some(declaration) = declaration else {
15995        return Some(DispatchExtensibility::Open);
15996    };
15997
15998    let mut saw_virtual_boundary = false;
15999    let mut stack = vec![declaration];
16000    while let Some(node) = stack.pop() {
16001        match node.kind() {
16002            "compound_statement" | "field_declaration_list" => continue,
16003            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16004            "virtual"
16005            | "override"
16006            | "virtual_specifier"
16007            | "pure_virtual_clause"
16008            | "template_parameter_list"
16009            | "template_method"
16010            | "template_function"
16011            | "ERROR" => saw_virtual_boundary = true,
16012            _ => {}
16013        }
16014        let mut cursor = node.walk();
16015        stack.extend(node.children(&mut cursor));
16016    }
16017
16018    if saw_virtual_boundary {
16019        return Some(DispatchExtensibility::Open);
16020    }
16021    if let Some(body) = class_body {
16022        // C++ inherits virtual-ness, so a member written without a `virtual`
16023        // keyword is still virtual when a base declares it. Whether some base
16024        // does is not decidable here, so a base clause keeps the answer open.
16025        let has_base_clause = ancestry
16026            .parent(body)
16027            .and_then(cpp_class_like_base_clause)
16028            .is_some();
16029        return Some(if has_base_clause {
16030            DispatchExtensibility::Open
16031        } else {
16032            DispatchExtensibility::Closed
16033        });
16034    }
16035    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16036        return None;
16037    }
16038    // A namespace or file scope callable is reached by name, never through a
16039    // receiver, so no other body can be selected for a call to it.
16040    Some(DispatchExtensibility::Closed)
16041}
16042
16043fn cpp_callable_linkage<'tree>(
16044    declaration: Node<'tree>,
16045    source: &str,
16046    ancestry: &ParentIndex<'tree>,
16047) -> CallableLinkage {
16048    let mut enclosed_by_class = false;
16049    let mut current = ancestry.parent(declaration);
16050    while let Some(node) = current {
16051        if node.kind() == "namespace_definition"
16052            && node
16053                .child_by_field_name("name")
16054                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16055        {
16056            return CallableLinkage::Internal;
16057        }
16058        if matches!(
16059            node.kind(),
16060            "class_specifier" | "struct_specifier" | "union_specifier"
16061        ) {
16062            if node
16063                .child_by_field_name("name")
16064                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16065            {
16066                return CallableLinkage::Internal;
16067            }
16068            enclosed_by_class = true;
16069        }
16070        // An export macro between `class` and the class name can make
16071        // tree-sitter parse the entire class as a function definition. The
16072        // structured recovery proves that this container is a named class
16073        // scope, not a local callable scope.
16074        if node.kind() == "lambda_expression"
16075            || node.kind() == "function_definition"
16076                && !is_recovered_exported_class_container(node, source)
16077        {
16078            return CallableLinkage::Internal;
16079        }
16080        current = ancestry.parent(node);
16081    }
16082
16083    if enclosed_by_class {
16084        return CallableLinkage::External;
16085    }
16086
16087    if cpp_declares_static_storage_class(declaration, source) {
16088        CallableLinkage::Internal
16089    } else {
16090        CallableLinkage::External
16091    }
16092}
16093
16094fn cpp_callable_return_type_text<'tree>(
16095    function_declarator: Node<'tree>,
16096    source: &str,
16097    ancestry: &ParentIndex<'tree>,
16098) -> Option<String> {
16099    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16100        return None;
16101    }
16102    if let Some((return_type, _)) =
16103        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16104    {
16105        let text = normalize_cpp_whitespace(node_text(return_type, source));
16106        return (!text.is_empty()).then_some(text);
16107    }
16108    let mut cursor = function_declarator.walk();
16109    if let Some(trailing) = function_declarator
16110        .named_children(&mut cursor)
16111        .find(|child| child.kind() == "trailing_return_type")
16112        && let Some(type_descriptor) = trailing.named_child(0)
16113    {
16114        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16115        if !text.is_empty() {
16116            return Some(text);
16117        }
16118    }
16119
16120    let mut current = function_declarator;
16121    let mut indirection = String::new();
16122    while let Some(parent) = ancestry.parent(current) {
16123        if matches!(
16124            parent.kind(),
16125            "function_definition" | "declaration" | "field_declaration"
16126        ) {
16127            let type_node = parent.child_by_field_name("type")?;
16128            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16129                return None;
16130            }
16131            let base = normalize_cpp_whitespace(node_text(type_node, source));
16132            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16133        }
16134        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16135            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16136                && parent.named_child_count() == 1
16137                && parent.named_child(0) == Some(current));
16138        if wraps_current_declarator {
16139            match parent.kind() {
16140                "pointer_declarator" => indirection.push('*'),
16141                "reference_declarator" => {
16142                    let reference = parent
16143                        .children(&mut parent.walk())
16144                        .find(|child| !child.is_named())
16145                        .map(|child| node_text(child, source))
16146                        .unwrap_or("&");
16147                    indirection.push_str(reference);
16148                }
16149                "init_declarator" | "parenthesized_declarator" => {}
16150                _ => return None,
16151            }
16152            current = parent;
16153            continue;
16154        }
16155        return None;
16156    }
16157    None
16158}
16159
16160pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16161    let mut required = 0;
16162    let mut total = 0;
16163    let mut repeated = false;
16164    for slot in cpp_callable_parameter_slots(parameters_node, source) {
16165        match slot {
16166            CppParameterSlot::Declared {
16167                required: is_required,
16168                ..
16169            } => {
16170                total += 1;
16171                if is_required {
16172                    required += 1;
16173                }
16174            }
16175            CppParameterSlot::OptionalVoid => total += 1,
16176            CppParameterSlot::Ellipsis => repeated = true,
16177        }
16178    }
16179    CallableArity::new(required, total, repeated)
16180}
16181
16182/// One entry of a callable's invocation parameter list, plus an optional
16183/// unnamed `void` declaration retained for the legacy arity result.
16184///
16185/// The list excludes an explicit object parameter and a lone `void`. Every
16186/// derivation of parameter arity or type starts from this same sequence, so
16187/// those views cannot disagree about which parameters exist.
16188#[derive(Clone, Copy)]
16189enum CppParameterSlot<'tree> {
16190    Declared {
16191        parameter: Node<'tree>,
16192        required: bool,
16193    },
16194    OptionalVoid,
16195    Ellipsis,
16196}
16197
16198fn cpp_callable_parameter_slots<'tree>(
16199    parameters_node: Node<'tree>,
16200    source: &str,
16201) -> Vec<CppParameterSlot<'tree>> {
16202    let mut slots = Vec::new();
16203    let mut cursor = parameters_node.walk();
16204    for parameter in parameters_node.children(&mut cursor) {
16205        match parameter.kind() {
16206            "parameter_declaration" | "optional_parameter_declaration" => {
16207                let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16208                    && parameter
16209                        .child_by_field_name("type")
16210                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16211                if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16212                    continue;
16213                }
16214                if is_unnamed_void {
16215                    slots.push(CppParameterSlot::OptionalVoid);
16216                    continue;
16217                }
16218                slots.push(CppParameterSlot::Declared {
16219                    parameter,
16220                    required: parameter.kind() == "parameter_declaration",
16221                });
16222            }
16223            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16224                slots.push(CppParameterSlot::Ellipsis);
16225            }
16226            _ => {}
16227        }
16228    }
16229    slots
16230}
16231
16232pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16233    cpp_callable_parameter_slots(parameters_node, source)
16234        .into_iter()
16235        .filter_map(|slot| match slot {
16236            CppParameterSlot::Declared { parameter, .. } => {
16237                Some(cpp_parameter_type(parameter, source))
16238            }
16239            CppParameterSlot::Ellipsis => Some("...".to_string()),
16240            CppParameterSlot::OptionalVoid => None,
16241        })
16242        .collect()
16243}
16244
16245/// Return the written name of each callable parameter in invocation order.
16246/// C and C++ permit declarations without names; those slots deliberately stay
16247/// `None` so consumers can distinguish an unnamed formal from a guessed one.
16248pub(crate) fn cpp_callable_parameter_names(
16249    parameters_node: Node<'_>,
16250    source: &str,
16251) -> Vec<Option<String>> {
16252    cpp_callable_parameter_slots(parameters_node, source)
16253        .into_iter()
16254        .filter_map(|slot| match slot {
16255            CppParameterSlot::Declared { parameter, .. } => Some(
16256                parameter
16257                    .child_by_field_name("declarator")
16258                    .and_then(crate::structural::declarator_name_node)
16259                    .and_then(|name| direct_identifier_name(name, source)),
16260            ),
16261            CppParameterSlot::Ellipsis => Some(None),
16262            CppParameterSlot::OptionalVoid => None,
16263        })
16264        .collect()
16265}
16266
16267/// One callable parameter's parser-derived type.
16268///
16269/// A rendered spelling such as `const T&` is a source text, not a type name. A
16270/// consumer that must publish a type into a structured model - a semantic-pack
16271/// type reference, for example - reads this instead.
16272#[derive(Debug, Clone, PartialEq, Eq)]
16273pub enum CppParameterType {
16274    /// The written type reduced to a structured identity. C++ cv-qualifiers
16275    /// have no place in that model and are not represented.
16276    Structured(StructuredTypeIdentity),
16277    /// A `...` pack, which declares no parameter type at all.
16278    Ellipsis,
16279    /// A written type with no structured reduction, such as a macro-obscured,
16280    /// `decltype`-computed, or function-pointer parameter.
16281    Unstructured,
16282}
16283
16284/// The structured type of each invocation parameter, in declaration order.
16285///
16286/// The result is index-parallel with the rendered
16287/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
16288/// callable.
16289pub fn cpp_callable_parameter_type_identities<'tree>(
16290    function_declarator: Node<'tree>,
16291    source: &str,
16292    ancestry: &ParentIndex<'tree>,
16293) -> Vec<CppParameterType> {
16294    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16295        return Vec::new();
16296    };
16297    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16298    cpp_callable_parameter_slots(parameters_node, source)
16299        .into_iter()
16300        .filter_map(|slot| match slot {
16301            CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16302            CppParameterSlot::Declared { parameter, .. } => Some(
16303                cpp_parameter_type_identity(parameter, source, &lexical_scope)
16304                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16305            ),
16306            CppParameterSlot::OptionalVoid => None,
16307        })
16308        .collect()
16309}
16310
16311/// The parser-derived type of one declared object or parameter.
16312///
16313/// The declaration owns the base `type` field while the individual declarator
16314/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
16315/// lets callers distinguish several declarators in one declaration without
16316/// reparsing a rendered signature.
16317pub fn cpp_declaration_type_identity<'tree>(
16318    declaration: Node<'tree>,
16319    declarator: Node<'tree>,
16320    source: &str,
16321    ancestry: &ParentIndex<'tree>,
16322) -> Option<StructuredTypeIdentity> {
16323    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16324    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16325}
16326
16327fn cpp_parameter_type_identity(
16328    parameter: Node<'_>,
16329    source: &str,
16330    lexical_scope: &[String],
16331) -> Option<StructuredTypeIdentity> {
16332    cpp_declaration_type_identity_in_scope(
16333        parameter,
16334        cpp_parameter_declarator(parameter),
16335        source,
16336        lexical_scope,
16337    )
16338}
16339
16340fn cpp_declaration_type_identity_in_scope(
16341    declaration: Node<'_>,
16342    declarator: Option<Node<'_>>,
16343    source: &str,
16344    lexical_scope: &[String],
16345) -> Option<StructuredTypeIdentity> {
16346    let type_node = declaration.child_by_field_name("type")?;
16347    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16348    if let Some(declarator) = declarator {
16349        return cpp_apply_structured_type_wrappers(
16350            identity,
16351            cpp_structured_declarator_wrappers(declarator)?,
16352        );
16353    }
16354    Some(identity)
16355}
16356
16357/// The comparable shape of each invocation parameter, in declaration order.
16358///
16359/// The result is index-parallel with
16360/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
16361/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
16362/// must treat as evidence of nothing rather than as agreement.
16363pub fn cpp_comparable_parameter_shapes<'tree>(
16364    function_declarator: Node<'tree>,
16365    source: &str,
16366    ancestry: &ParentIndex<'tree>,
16367) -> Vec<CppComparableSlot> {
16368    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16369        return Vec::new();
16370    };
16371    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16372    cpp_callable_parameter_slots(parameters_node, source)
16373        .into_iter()
16374        .filter_map(|slot| match slot {
16375            CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16376            CppParameterSlot::Declared { parameter, .. } => Some(
16377                cpp_comparable_parameter(parameter, source, &lexical_scope)
16378                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16379            ),
16380            CppParameterSlot::OptionalVoid => None,
16381        })
16382        .collect()
16383}
16384
16385fn cpp_comparable_parameter(
16386    parameter: Node<'_>,
16387    source: &str,
16388    lexical_scope: &[String],
16389) -> Option<CppComparableParameter> {
16390    let type_node = parameter.child_by_field_name("type")?;
16391    let levels = match cpp_parameter_declarator(parameter) {
16392        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16393        None => Vec::new(),
16394    };
16395    let mut shape = cpp_comparable_type_shape(
16396        type_node,
16397        cpp_cv_qualifiers(parameter, source),
16398        levels,
16399        source,
16400        lexical_scope,
16401    )?;
16402    shape.adjust_parameter_top_level();
16403    Some(shape)
16404}
16405
16406/// The `const` and `volatile` qualifiers written as direct named children of
16407/// `node`.
16408///
16409/// The grammar exposes `type_qualifier` as a non-field named child in exactly
16410/// the three places a parameter's qualifiers can be written: on the
16411/// `parameter_declaration` itself (the base type), on a `type_descriptor`
16412/// (inside a template argument list), and on each `pointer_declarator` level
16413/// (the pointer object). Every other qualifier the grammar admits - `restrict`
16414/// and friends - takes no part in C++ type identity, the same filter
16415/// `cpp_parameter_type` applies to the rendered spelling (#1827).
16416fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16417    let mut qualifiers = CppCvQualifiers::default();
16418    let mut cursor = node.walk();
16419    for child in node.named_children(&mut cursor) {
16420        if child.kind() != "type_qualifier" {
16421            continue;
16422        }
16423        match node_text(child, source) {
16424            "const" => qualifiers.konst = true,
16425            "volatile" => qualifiers.volatil = true,
16426            _ => {}
16427        }
16428    }
16429    qualifiers
16430}
16431
16432#[derive(Clone, Copy, Default)]
16433struct CppCvQualifiers {
16434    konst: bool,
16435    volatil: bool,
16436}
16437
16438impl CppCvQualifiers {
16439    fn union(self, other: Self) -> Self {
16440        Self {
16441            konst: self.konst || other.konst,
16442            volatil: self.volatil || other.volatil,
16443        }
16444    }
16445}
16446
16447/// One pointer, reference or array level a declarator chain adds.
16448#[derive(Clone, Copy)]
16449enum CppComparableLevel {
16450    Pointer { konst: bool, volatil: bool },
16451    Reference,
16452    Array,
16453}
16454
16455/// The levels `declarator` adds, outermost written level first.
16456///
16457/// C++ declarator syntax binds inside out: the level written closest to the
16458/// declared name is the outermost type constructor, and tree-sitter nests it
16459/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
16460/// applies in order to reach "array of pointer to int", and the qualifier of
16461/// `int * const *p` is read on the level it was written next to, the inner
16462/// pointer of the resulting type.
16463///
16464/// A declarator chain that names a function type - a function-pointer
16465/// parameter - has no comparable shape and reports `None`, matching the
16466/// structured identity channel.
16467fn cpp_comparable_declarator_levels(
16468    declarator: Node<'_>,
16469    source: &str,
16470) -> Option<Vec<CppComparableLevel>> {
16471    let mut levels = Vec::new();
16472    let mut current = declarator;
16473    loop {
16474        match current.kind() {
16475            "pointer_declarator" | "abstract_pointer_declarator" => {
16476                let qualifiers = cpp_cv_qualifiers(current, source);
16477                levels.push(CppComparableLevel::Pointer {
16478                    konst: qualifiers.konst,
16479                    volatil: qualifiers.volatil,
16480                });
16481            }
16482            "reference_declarator" | "abstract_reference_declarator" => {
16483                levels.push(CppComparableLevel::Reference);
16484            }
16485            "array_declarator" | "abstract_array_declarator" => {
16486                levels.push(CppComparableLevel::Array);
16487            }
16488            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16489            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16490            _ => return None,
16491        }
16492        let Some(next) = cpp_nested_declarator(current) else {
16493            return Some(levels);
16494        };
16495        current = next;
16496    }
16497}
16498
16499/// Reduce one written type to a comparable arena.
16500///
16501/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
16502/// two additions: each visited type node carries the cv-qualifiers written on
16503/// it, and declarator levels arrive as a prepared list rather than being
16504/// rediscovered inside the walk.
16505fn cpp_comparable_type_shape(
16506    type_node: Node<'_>,
16507    qualifiers: CppCvQualifiers,
16508    levels: Vec<CppComparableLevel>,
16509    source: &str,
16510    lexical_scope: &[String],
16511) -> Option<CppComparableParameter> {
16512    enum Work<'tree> {
16513        Visit {
16514            node: Node<'tree>,
16515            qualifiers: CppCvQualifiers,
16516        },
16517        ApplyLevels(Vec<CppComparableLevel>),
16518        BuildGeneric {
16519            argument_count: usize,
16520        },
16521    }
16522
16523    let mut nodes: Vec<CppComparableNode> = Vec::new();
16524    let mut values: Vec<usize> = Vec::new();
16525    let mut work = vec![
16526        Work::ApplyLevels(levels),
16527        Work::Visit {
16528            node: type_node,
16529            qualifiers,
16530        },
16531    ];
16532    while let Some(next) = work.pop() {
16533        match next {
16534            Work::Visit { node, qualifiers } => match node.kind() {
16535                "type_descriptor" => {
16536                    let inner_type = node
16537                        .child_by_field_name("type")
16538                        .or_else(|| node.named_child(0))?;
16539                    let mut cursor = node.walk();
16540                    let declarator = node.child_by_field_name("declarator").or_else(|| {
16541                        node.named_children(&mut cursor).find(|child| {
16542                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
16543                        })
16544                    });
16545                    let levels = match declarator {
16546                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16547                        None => Vec::new(),
16548                    };
16549                    work.push(Work::ApplyLevels(levels));
16550                    work.push(Work::Visit {
16551                        node: inner_type,
16552                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16553                    });
16554                }
16555                "sized_type_specifier" => {
16556                    // `unsigned char` is one primitive type whose components are
16557                    // partly unnamed tokens, so the whole specifier is its own
16558                    // name component. Reducing it to the `type` child would make
16559                    // `f(unsigned char)` and `f(char)` compare equal.
16560                    let name = StructuredTypeName::new(
16561                        vec![normalize_cpp_whitespace(node_text(node, source))],
16562                        lexical_scope.to_vec(),
16563                        false,
16564                    )?;
16565                    values.push(cpp_push_comparable_node(
16566                        &mut nodes,
16567                        CppComparableNode::Named {
16568                            name,
16569                            primitive: true,
16570                            konst: qualifiers.konst,
16571                            volatil: qualifiers.volatil,
16572                        },
16573                    ));
16574                }
16575                "qualified_identifier"
16576                | "scoped_identifier"
16577                | "scoped_type_identifier"
16578                | "type_identifier"
16579                | "field_identifier"
16580                | "identifier"
16581                | "namespace_identifier"
16582                | "primitive_type"
16583                | "template_type" => {
16584                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
16585                    values.push(cpp_push_comparable_node(
16586                        &mut nodes,
16587                        CppComparableNode::Named {
16588                            name,
16589                            primitive: node.kind() == "primitive_type",
16590                            konst: qualifiers.konst,
16591                            volatil: qualifiers.volatil,
16592                        },
16593                    ));
16594                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16595                        let mut cursor = arguments_node.walk();
16596                        let arguments = arguments_node
16597                            .named_children(&mut cursor)
16598                            .filter(|child| !child.is_extra() && child.kind() != "comment")
16599                            .collect::<Vec<_>>();
16600                        work.push(Work::BuildGeneric {
16601                            argument_count: arguments.len(),
16602                        });
16603                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16604                            node: argument,
16605                            qualifiers: CppCvQualifiers::default(),
16606                        }));
16607                    }
16608                }
16609                _ => {
16610                    let inner = node.child_by_field_name("type").or_else(|| {
16611                        (node.named_child_count() == 1)
16612                            .then(|| node.named_child(0))
16613                            .flatten()
16614                    })?;
16615                    work.push(Work::Visit {
16616                        node: inner,
16617                        qualifiers,
16618                    });
16619                }
16620            },
16621            Work::ApplyLevels(levels) => {
16622                let mut root = values.pop()?;
16623                for level in levels {
16624                    let node = match level {
16625                        CppComparableLevel::Pointer { konst, volatil } => {
16626                            CppComparableNode::Pointer {
16627                                inner: root,
16628                                konst,
16629                                volatil,
16630                            }
16631                        }
16632                        CppComparableLevel::Reference => {
16633                            CppComparableNode::Reference { inner: root }
16634                        }
16635                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16636                    };
16637                    root = cpp_push_comparable_node(&mut nodes, node);
16638                }
16639                values.push(root);
16640            }
16641            Work::BuildGeneric { argument_count } => {
16642                let value_count = argument_count.checked_add(1)?;
16643                let start = values.len().checked_sub(value_count)?;
16644                let mut built = values.split_off(start);
16645                let base = built.remove(0);
16646                values.push(cpp_push_comparable_node(
16647                    &mut nodes,
16648                    CppComparableNode::Generic {
16649                        base,
16650                        arguments: built,
16651                    },
16652                ));
16653            }
16654        }
16655    }
16656    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16657    debug_assert_eq!(
16658        root,
16659        nodes.len().saturating_sub(1),
16660        "comparable nodes are appended in post-order, so the root is the last one"
16661    );
16662    Some(CppComparableParameter::new(nodes, root))
16663}
16664
16665fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16666    nodes.push(node);
16667    nodes.len() - 1
16668}
16669
16670/// The template argument list of the name `node` terminates in, if any.
16671///
16672/// `std::vector<int>` writes its arguments on the `name` of a qualified
16673/// identifier, so a walk that stopped at the qualified node would reduce
16674/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
16675fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16676    let mut current = node;
16677    loop {
16678        match current.kind() {
16679            "template_type" => return current.child_by_field_name("arguments"),
16680            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16681                current = current.child_by_field_name("name")?;
16682            }
16683            _ => return None,
16684        }
16685    }
16686}
16687
16688/// The callable declarator of the declaration that covers `start_byte`.
16689///
16690/// A consumer that holds a declaration's recorded byte position rather than its
16691/// syntax node - external header extraction, for instance - uses this to reach
16692/// the same `function_declarator` the declaration walk read.
16693pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16694    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16695    loop {
16696        if matches!(
16697            current.kind(),
16698            "declaration" | "field_declaration" | "function_definition"
16699        ) && let Some(declarator) = current
16700            .child_by_field_name("declarator")
16701            .and_then(extract_function_declarator)
16702        {
16703            return Some(declarator);
16704        }
16705        current = current.parent()?;
16706    }
16707}
16708
16709fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16710    let mut labels = Vec::new();
16711    let mut cursor = parameters_node.walk();
16712    for child in parameters_node.children(&mut cursor) {
16713        match child.kind() {
16714            "parameter_declaration" | "optional_parameter_declaration" => {
16715                if let Some(name_node) = child
16716                    .child_by_field_name("declarator")
16717                    .and_then(cpp_declarator_label_node)
16718                {
16719                    labels.push(name_node);
16720                } else {
16721                    labels.push(child);
16722                }
16723            }
16724            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16725                labels.push(child);
16726            }
16727            _ => {}
16728        }
16729    }
16730    labels
16731}
16732
16733fn cpp_signature_search_start<'tree>(
16734    signature: &str,
16735    function_declarator: Node<'tree>,
16736    source: &str,
16737    ancestry: &ParentIndex<'tree>,
16738) -> usize {
16739    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16740        return 0;
16741    };
16742    let raw = node_text(enclosing, source);
16743    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16744    let offset = function_declarator
16745        .start_byte()
16746        .saturating_sub(enclosing.start_byte())
16747        .saturating_sub(leading_trim_bytes);
16748    offset.min(signature.len())
16749}
16750
16751fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16752    match node.kind() {
16753        "identifier" | "field_identifier" => Some(node),
16754        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16755            .child_by_field_name("declarator")
16756            .or_else(|| last_named_child(node))
16757            .and_then(cpp_declarator_label_node),
16758        "array_declarator" => node
16759            .child_by_field_name("declarator")
16760            .and_then(cpp_declarator_label_node),
16761        "function_declarator" => node
16762            .child_by_field_name("declarator")
16763            .or_else(|| node.child_by_field_name("name"))
16764            .or_else(|| last_named_child(node))
16765            .and_then(cpp_declarator_label_node),
16766        _ => None,
16767    }
16768}
16769
16770fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16771    let base_type = parameter
16772        .child_by_field_name("type")
16773        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16774        .unwrap_or_default();
16775    let declarator = cpp_parameter_declarator(parameter);
16776    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
16777    // are discarded, so `f(const int)` and `f(int)` declare one function. A
16778    // qualifier written next to the parameter's type is only top-level when
16779    // the declarator adds no indirection; behind a pointer, reference or array
16780    // declarator the same qualifier belongs to the pointee, referent or
16781    // element and keeps distinguishing the type (#1827).
16782    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16783    let mut cursor = parameter.walk();
16784    let qualifiers = parameter
16785        .named_children(&mut cursor)
16786        .filter(|child| child.kind() == "type_qualifier")
16787        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16788        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16789        .collect::<Vec<_>>()
16790        .join(" ");
16791    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16792        (true, _) => base_type,
16793        (_, true) => qualifiers,
16794        (false, false) => format!("{qualifiers} {base_type}"),
16795    };
16796    let declarator_suffix = declarator
16797        .map(|node| cpp_declarator_suffix_without_name(node, source))
16798        .unwrap_or_default();
16799
16800    let combined = if type_text.is_empty() {
16801        declarator_suffix
16802    } else if declarator_suffix.is_empty() {
16803        type_text
16804    } else {
16805        format!("{type_text} {declarator_suffix}")
16806    };
16807    normalize_cpp_type_text(&combined)
16808}
16809
16810fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16811    parameter.child_by_field_name("declarator").or_else(|| {
16812        // Some unnamed prototype parameters expose their abstract declarator
16813        // as a direct named child without the grammar's `declarator` field.
16814        // Recover only the structured abstract-declarator node; the parameter's
16815        // type and qualifiers are distinct children and must not be guessed from
16816        // source text.
16817        cpp_first_abstract_declarator_child(parameter)
16818    })
16819}
16820
16821/// Whether a parameter's declarator chain adds indirection - a pointer,
16822/// reference, array or function declarator - to the parameter's written type.
16823pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16824    let mut current = Some(declarator);
16825    while let Some(node) = current {
16826        if matches!(
16827            node.kind(),
16828            "pointer_declarator"
16829                | "abstract_pointer_declarator"
16830                | "reference_declarator"
16831                | "abstract_reference_declarator"
16832                | "array_declarator"
16833                | "abstract_array_declarator"
16834                | "function_declarator"
16835                | "abstract_function_declarator"
16836        ) {
16837            return true;
16838        }
16839        current = cpp_nested_declarator(node);
16840    }
16841    false
16842}
16843
16844fn is_cpp_abstract_declarator(kind: &str) -> bool {
16845    matches!(
16846        kind,
16847        "abstract_pointer_declarator"
16848            | "abstract_reference_declarator"
16849            | "abstract_array_declarator"
16850            | "abstract_function_declarator"
16851            | "abstract_parenthesized_declarator"
16852    )
16853}
16854
16855fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16856    let mut cursor = node.walk();
16857    node.named_children(&mut cursor)
16858        .find(|child| is_cpp_abstract_declarator(child.kind()))
16859}
16860
16861fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16862    node.child_by_field_name("declarator").or_else(|| {
16863        if is_cpp_abstract_declarator(node.kind()) {
16864            cpp_first_abstract_declarator_child(node)
16865        } else {
16866            // Named declarators historically use their last named child when
16867            // tree-sitter omits the field. Keep that broad fallback for
16868            // attributed, variadic, and recovered named shapes.
16869            last_named_child(node)
16870        }
16871    })
16872}
16873
16874pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
16875    matches!(
16876        node.kind(),
16877        "reference_declarator" | "abstract_reference_declarator"
16878    ) && node
16879        .children(&mut node.walk())
16880        .any(|child| child.kind() == "&&")
16881}
16882
16883fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
16884    match node.kind() {
16885        "identifier" | "field_identifier" => String::new(),
16886        "pointer_declarator" | "abstract_pointer_declarator" => {
16887            let inner = cpp_nested_declarator(node)
16888                .map(|child| cpp_declarator_suffix_without_name(child, source))
16889                .unwrap_or_default();
16890            format!("*{inner}")
16891        }
16892        "reference_declarator" | "abstract_reference_declarator" => {
16893            let inner = cpp_nested_declarator(node)
16894                .map(|child| cpp_declarator_suffix_without_name(child, source))
16895                .unwrap_or_default();
16896            let reference = if reference_declarator_is_rvalue(node) {
16897                "&&"
16898            } else {
16899                "&"
16900            };
16901            format!("{reference}{inner}")
16902        }
16903        "array_declarator" | "abstract_array_declarator" => {
16904            let inner = cpp_nested_declarator(node)
16905                .map(|child| cpp_declarator_suffix_without_name(child, source))
16906                .unwrap_or_default();
16907            let size = node
16908                .child_by_field_name("size")
16909                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16910                .unwrap_or_default();
16911            format!("{inner}[{size}]")
16912        }
16913        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
16914            let inner = cpp_nested_declarator(node);
16915            inner
16916                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
16917                .unwrap_or_default()
16918        }
16919        "function_declarator" | "abstract_function_declarator" => {
16920            let inner = cpp_nested_declarator(node)
16921                .map(|child| cpp_declarator_suffix_without_name(child, source))
16922                .unwrap_or_default();
16923            let params = node
16924                .child_by_field_name("parameters")
16925                .map(|child| cpp_parameter_signature(child, source))
16926                .unwrap_or_else(|| "()".to_string());
16927            format!("{inner}{params}")
16928        }
16929        _ => {
16930            let text = normalize_cpp_whitespace(node_text(node, source));
16931            let name = extract_declarator_name(node, source);
16932            if name.is_empty() {
16933                text
16934            } else {
16935                text.replace(&name, "").trim().to_string()
16936            }
16937        }
16938    }
16939}
16940
16941fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
16942    collapse_cpp_whitespace(
16943        suffix
16944            .trim()
16945            .trim_start_matches("->")
16946            .trim_start_matches('{')
16947            .trim_end_matches(';'),
16948    )
16949}
16950
16951pub fn normalize_cpp_whitespace(value: &str) -> String {
16952    collapse_cpp_whitespace(value)
16953}
16954
16955fn normalize_cpp_type_text(value: &str) -> String {
16956    collapse_cpp_whitespace(value)
16957        .replace(", ", ",")
16958        .replace(" <", "<")
16959        .replace("< ", "<")
16960        .replace(" >", ">")
16961}
16962
16963fn collapse_cpp_whitespace(value: &str) -> String {
16964    let mut result = String::new();
16965    let mut prev_space = false;
16966    for ch in value.chars() {
16967        if ch.is_whitespace() {
16968            if !prev_space {
16969                result.push(' ');
16970            }
16971            prev_space = true;
16972        } else {
16973            result.push(ch);
16974            prev_space = false;
16975        }
16976    }
16977    result.trim().to_string()
16978}
16979
16980fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
16981    child_by_field_or_named_child_of_kinds(
16982        node,
16983        "body",
16984        &[
16985            "declaration_list",
16986            "field_declaration_list",
16987            "enumerator_list",
16988        ],
16989    )
16990}
16991
16992/// Return a class body's actual closing brace when the parser supplied one.
16993///
16994/// A malformed namespace sentinel can leave a class node carrying unrelated
16995/// parser errors even though its own class body is complete.  `has_error()` is
16996/// therefore too coarse an admission predicate for sentinel ownership.  The
16997/// body list, however, exposes the opening and closing punctuation directly;
16998/// a real (non-missing) final `}` proves that the class did not borrow the
16999/// enclosing namespace's close.  Requiring the body to end before its parent
17000/// container also rejects a recovered node whose body swallowed that outer
17001/// boundary.
17002fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17003    if !matches!(
17004        node.kind(),
17005        "class_specifier" | "struct_specifier" | "union_specifier"
17006    ) {
17007        return None;
17008    }
17009    let body = cpp_body_node(node)?;
17010    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17011        return None;
17012    }
17013    let open = body.child(0)?;
17014    let close = body.child(body.child_count().checked_sub(1)?)?;
17015    if open.kind() != "{"
17016        || open.is_missing()
17017        || close.kind() != "}"
17018        || close.is_missing()
17019        || close.end_byte() != body.end_byte()
17020        || body.end_byte() > node.end_byte()
17021        || node
17022            .parent()
17023            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17024    {
17025        return None;
17026    }
17027    Some(close)
17028}
17029
17030fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17031    let mut stack = vec![node];
17032    while let Some(node) = stack.pop() {
17033        if node.kind() == "namespace_definition" {
17034            return true;
17035        }
17036        push_named_children_reversed(node, &mut stack);
17037    }
17038    false
17039}
17040
17041struct CppNestedNamespaceSentinel<'tree> {
17042    function: Node<'tree>,
17043    body: Node<'tree>,
17044    namespace_components: Vec<String>,
17045}
17046
17047/// Owned structural recovery metadata for a namespace-sentinel region.
17048///
17049/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
17050/// instead of the namespace/class scopes that the declaration visitor restores.
17051/// The inverted usage walk has the original CST, so it needs the same ownership
17052/// evidence without borrowing parser nodes across its file scan.  Keep this
17053/// descriptor deliberately source-range based: callers can match a reference
17054/// node by containment and then resolve its structured type spelling in the
17055/// recovered class scope.
17056#[derive(Debug, Clone)]
17057pub struct CppSentinelRecoveredOwner {
17058    pub range: Range,
17059    /// Start of the qualified owner name (`btree<P>::method`).  A leading
17060    /// return type before this byte is looked up from the namespace; parameters,
17061    /// trailing returns, and the body use the member owner scope.
17062    pub owner_name_start_byte: usize,
17063    /// Number of leading components belonging to the namespace rather than
17064    /// the qualified class owner.  A leading return type is looked up before
17065    /// every owner component, not merely before the innermost class.
17066    pub namespace_component_count: usize,
17067    pub scope_components: Vec<String>,
17068}
17069
17070#[derive(Debug, Clone)]
17071pub struct CppSentinelRecoveredClass {
17072    pub namespace_range: Range,
17073    pub namespace_scope_components: Vec<String>,
17074    pub class_range: Range,
17075    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
17076    pub scope_components: Vec<String>,
17077    /// Qualified out-of-line member definitions owned by this class.  Their
17078    /// ranges may extend beyond `class_range` when the malformed sentinel
17079    /// swallowed the namespace close and left definitions as function siblings.
17080    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17081}
17082
17083/// Resolve the lexical scope restored for a node in a malformed
17084/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
17085/// outrank class spans, which in turn outrank the surviving namespace body.
17086/// The class ancestor suffix is recovered from the original CST so nested
17087/// members keep their complete `Outer::Inner` owner chain.
17088pub fn cpp_sentinel_recovered_scope_for_node(
17089    node: Node<'_>,
17090    source: &str,
17091    recovered_classes: &[CppSentinelRecoveredClass],
17092) -> Option<Vec<String>> {
17093    let contains =
17094        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17095    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17096    for recovered in recovered_classes {
17097        for owner in recovered
17098            .owner_ranges
17099            .iter()
17100            .filter(|owner| contains(owner.range))
17101        {
17102            let replace = best_owner.is_none_or(|existing| {
17103                owner.range.end_byte.saturating_sub(owner.range.start_byte)
17104                    < existing
17105                        .range
17106                        .end_byte
17107                        .saturating_sub(existing.range.start_byte)
17108            });
17109            if replace {
17110                best_owner = Some(owner);
17111            }
17112        }
17113    }
17114    if let Some(owner) = best_owner {
17115        let mut scope = owner.scope_components.clone();
17116        if node.start_byte() < owner.owner_name_start_byte {
17117            scope.truncate(owner.namespace_component_count);
17118        }
17119        return Some(scope);
17120    }
17121
17122    let class = recovered_classes
17123        .iter()
17124        .filter(|recovered| contains(recovered.class_range))
17125        .min_by_key(|recovered| {
17126            recovered
17127                .class_range
17128                .end_byte
17129                .saturating_sub(recovered.class_range.start_byte)
17130        });
17131    let class_scope = class.is_some();
17132    let mut scope = if let Some(class) = class {
17133        class.scope_components.clone()
17134    } else {
17135        let namespace = recovered_classes
17136            .iter()
17137            .filter(|recovered| contains(recovered.namespace_range))
17138            .min_by_key(|recovered| {
17139                recovered
17140                    .namespace_range
17141                    .end_byte
17142                    .saturating_sub(recovered.namespace_range.start_byte)
17143            })?;
17144        let mut scope = namespace.namespace_scope_components.clone();
17145        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17146        let common_prefix = scope
17147            .iter()
17148            .zip(&parser_namespace)
17149            .take_while(|(recovered, parser)| recovered == parser)
17150            .count();
17151        scope.extend(parser_namespace.into_iter().skip(common_prefix));
17152        scope
17153    };
17154    if class_scope {
17155        let mut ancestor_components = Vec::new();
17156        let mut ancestor = node.parent();
17157        while let Some(current) = ancestor {
17158            if matches!(
17159                current.kind(),
17160                "class_specifier" | "struct_specifier" | "union_specifier"
17161            ) && let Some(name) = current.child_by_field_name("name")
17162                && let Some(name_components) = cpp_name_components(name, source)
17163            {
17164                ancestor_components.push(
17165                    name_components
17166                        .into_iter()
17167                        .map(|component| component.name)
17168                        .collect::<Vec<_>>(),
17169                );
17170            }
17171            ancestor = current.parent();
17172        }
17173        ancestor_components.reverse();
17174        let base_len = scope.len();
17175        for component in ancestor_components.into_iter().flatten() {
17176            if scope.len() >= base_len && scope.last() == Some(&component) {
17177                continue;
17178            }
17179            scope.push(component);
17180        }
17181    }
17182    Some(scope)
17183}
17184
17185struct CppSentinelFragmentedClassTail<'tree> {
17186    class_node: Node<'tree>,
17187    template_node: Option<Node<'tree>>,
17188    name: String,
17189    raw_supertypes: Option<Vec<String>>,
17190    fragmented: FragmentedExportBody,
17191    consumed_start: usize,
17192    /// The original node whose direct children after the class open are the
17193    /// class's members: the body of a class node, or the class-prefix `ERROR`
17194    /// itself when the class keyword, name, and `{` are its direct tokens.
17195    member_container: Node<'tree>,
17196}
17197
17198struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17199    name: String,
17200    open: Node<'tree>,
17201    raw_supertypes: Option<Vec<String>>,
17202}
17203
17204struct CppSentinelDirectBodyClassRegion {
17205    namespace_components: Vec<String>,
17206    class_start: usize,
17207    class_start_line: usize,
17208    class_close_end: usize,
17209    class_close_line: usize,
17210    name: String,
17211}
17212
17213fn cpp_sentinel_body_class_candidate<'tree>(
17214    child: Node<'tree>,
17215) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17216    if matches!(
17217        child.kind(),
17218        "class_specifier" | "struct_specifier" | "union_specifier"
17219    ) {
17220        return Some((child, None));
17221    }
17222    if child.kind() != "template_declaration" {
17223        if child.kind() == "declaration" {
17224            return Some((first_class_like_child(child)?, None));
17225        }
17226        return None;
17227    }
17228    let mut cursor = child.walk();
17229    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17230        if matches!(
17231            candidate.kind(),
17232            "class_specifier" | "struct_specifier" | "union_specifier"
17233        ) {
17234            Some(candidate)
17235        } else if candidate.kind() == "declaration" {
17236            first_class_like_child(candidate)
17237        } else {
17238            None
17239        }
17240    })?;
17241    Some((class_node, Some(child)))
17242}
17243
17244/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
17245/// namespace-sentinel body when a later member macro ends the bogus sentinel
17246/// function before the real class close. The anonymous class/open tokens and
17247/// direct identifier are the structural proof; a retained direct close would
17248/// be an ordinary malformed class rather than the fragmented tail handled here.
17249fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17250    node: Node<'tree>,
17251    source: &str,
17252) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17253    let name = malformed_class_error_owner_name(node, source)?;
17254    let mut cursor = node.walk();
17255    let children = node.children(&mut cursor).collect::<Vec<_>>();
17256    let keyword = children.first()?;
17257    let open_index = children.iter().position(|child| child.kind() == "{")?;
17258    if children[open_index + 1..]
17259        .iter()
17260        .any(|child| child.kind() == "}")
17261    {
17262        return None;
17263    }
17264    let raw_supertypes =
17265        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17266    Some(CppSentinelFragmentedClassErrorPrefix {
17267        name,
17268        open: children[open_index],
17269        raw_supertypes,
17270    })
17271}
17272
17273fn cpp_sentinel_direct_body_class_candidate<'tree>(
17274    child: Node<'tree>,
17275) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17276    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17277        return Some(candidate);
17278    }
17279    if child.kind() != "template_declaration" {
17280        return None;
17281    }
17282    let mut cursor = child.walk();
17283    let wrapper = child
17284        .named_children(&mut cursor)
17285        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17286    Some((first_class_like_child(wrapper)?, Some(child)))
17287}
17288
17289fn cpp_sentinel_direct_namespace_components(
17290    function: Node<'_>,
17291    body: Node<'_>,
17292    source: &str,
17293) -> Option<Vec<String>> {
17294    let mut cursor = function.walk();
17295    let children = function
17296        .named_children(&mut cursor)
17297        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17298        .collect::<Vec<_>>();
17299    let sentinel_index = children.iter().rposition(|child| {
17300        direct_identifier_name(*child, source)
17301            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17302    })?;
17303    let mut identifiers = Vec::new();
17304    let mut stack = children[sentinel_index + 1..]
17305        .iter()
17306        .rev()
17307        .copied()
17308        .collect::<Vec<_>>();
17309    while let Some(current) = stack.pop() {
17310        if let Some(name) = direct_identifier_name(current, source) {
17311            identifiers.push(name);
17312            continue;
17313        }
17314        let mut cursor = current.walk();
17315        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17316        stack.extend(children.into_iter().rev());
17317    }
17318    let [keyword, namespace] = identifiers.as_slice() else {
17319        return None;
17320    };
17321    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17322        .then(|| vec![namespace.clone()])
17323}
17324
17325fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17326    let mut sibling = class_semicolon.next_named_sibling();
17327    let namespace_close = loop {
17328        let Some(current) = sibling else {
17329            return false;
17330        };
17331        sibling = current.next_named_sibling();
17332        if current.kind() != "comment" {
17333            break current;
17334        }
17335    };
17336    if !cpp_is_stray_close_brace(namespace_close, source) {
17337        return false;
17338    }
17339    loop {
17340        let Some(current) = sibling else {
17341            return false;
17342        };
17343        sibling = current.next_named_sibling();
17344        if current.kind() == "comment" {
17345            continue;
17346        }
17347        return direct_identifier_name(current, source)
17348            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17349    }
17350}
17351
17352fn cpp_sentinel_macro_body_class_region<'tree>(
17353    node: Node<'tree>,
17354    source: &str,
17355    ancestry: &ParentIndex<'tree>,
17356) -> Option<CppSentinelDirectBodyClassRegion> {
17357    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17358        return None;
17359    };
17360    if node.kind() != "function_definition" || !node.has_error() {
17361        return None;
17362    }
17363    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17364    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17365    let mut cursor = body.walk();
17366    let candidates = body
17367        .named_children(&mut cursor)
17368        .filter_map(cpp_sentinel_direct_body_class_candidate)
17369        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17370        .collect::<Vec<_>>();
17371    let [(class_node, template_node)] = candidates.as_slice() else {
17372        return None;
17373    };
17374    let original_body = cpp_body_node(*class_node)?;
17375    let name = class_like_name(*class_node, source, ancestry)?;
17376    if name.is_empty() {
17377        return None;
17378    }
17379
17380    let mut sibling = node.next_named_sibling();
17381    let (class_close_start, class_close_end, class_close_line) = loop {
17382        let current = sibling?;
17383        let next = current.next_named_sibling();
17384        if cpp_is_stray_close_brace(current, source)
17385            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17386        {
17387            let semicolon = next.expect("checked above");
17388            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17389                return None;
17390            }
17391            break (
17392                current.start_byte(),
17393                semicolon.end_byte(),
17394                semicolon.end_position().row + 1,
17395            );
17396        }
17397        sibling = next;
17398    };
17399    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17400    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17401    let root = tree.root_node();
17402    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17403    // The region reparse is its own tree, so it needs its own parent index;
17404    // the caller's index answers nothing about these nodes.
17405    let reparsed_ancestry = ParentIndex::new(root);
17406    let reparsed =
17407        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17408    if reparsed.name != name
17409        || reparsed.declaration_node.start_byte() != class_node.start_byte()
17410        || reparsed.body.start_byte() != original_body.start_byte()
17411        || class_close_start <= reparsed.body.end_byte()
17412        || class_close_end <= class_node.end_byte()
17413    {
17414        return None;
17415    }
17416    Some(CppSentinelDirectBodyClassRegion {
17417        namespace_components,
17418        class_start: reparse_start,
17419        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17420            node.start_position().row + 1
17421        }),
17422        class_close_end,
17423        class_close_line,
17424        name,
17425    })
17426}
17427
17428/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
17429/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
17430///
17431/// The parser puts the namespace opener and the malformed function in one root
17432/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
17433/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
17434/// the malformed function must begin with an all-caps type, then an ERROR whose
17435/// sole identifier is `namespace`, followed by the inner namespace identifier
17436/// and a compound body; and that body must contain a complete named class or a
17437/// structurally fragmented class prefix. A text reparse cannot prove any of
17438/// those ownership boundaries.
17439fn cpp_nested_namespace_sentinel<'tree>(
17440    node: Node<'tree>,
17441    source: &str,
17442    ancestry: &ParentIndex<'tree>,
17443) -> Option<CppNestedNamespaceSentinel<'tree>> {
17444    // The shape checks below are the admission proof: a `type_identifier`
17445    // sentinel followed by an `ERROR` holding the `namespace` keyword implies
17446    // parser damage on its own, and under the macro-declaration grammar the
17447    // `macro_modifier` wrapper and the keyword in the type field are each
17448    // admitted only where the parse would otherwise contain an error.
17449    let (function, mut namespace_components) = if node.kind() == "ERROR" {
17450        let mut cursor = node.walk();
17451        let functions = node
17452            .named_children(&mut cursor)
17453            .filter(|child| child.kind() == "function_definition")
17454            .collect::<Vec<_>>();
17455        let [function] = functions.as_slice() else {
17456            return None;
17457        };
17458        let mut cursor = node.walk();
17459        let children = node.children(&mut cursor).collect::<Vec<_>>();
17460        let function_index = children
17461            .iter()
17462            .position(|child| same_node(*child, *function))?;
17463        let [outer_keyword, outer_name, outer_open] =
17464            children.get(function_index.checked_sub(3)?..function_index)?
17465        else {
17466            return None;
17467        };
17468        if outer_keyword.kind() != "namespace"
17469            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17470            || outer_open.kind() != "{"
17471        {
17472            return None;
17473        }
17474        (
17475            *function,
17476            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17477        )
17478    } else if node.kind() == "function_definition" {
17479        let declaration_list = node.parent()?;
17480        let namespace = declaration_list.parent()?;
17481        if declaration_list.kind() != "declaration_list"
17482            || namespace.kind() != "namespace_definition"
17483            || namespace.child_by_field_name("body") != Some(declaration_list)
17484        {
17485            return None;
17486        }
17487        (node, Vec::new())
17488    } else {
17489        return None;
17490    };
17491
17492    let mut cursor = function.walk();
17493    let named = function
17494        .named_children(&mut cursor)
17495        .filter(|child| child.kind() != "comment")
17496        .collect::<Vec<_>>();
17497    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17498        return None;
17499    };
17500    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
17501    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
17502    // the same token is wrapped in.
17503    let sentinel = if first_type.kind() == "type_identifier" {
17504        normalize_cpp_whitespace(node_text(*first_type, source))
17505    } else {
17506        let identifier = macro_modifier_identifier(*first_type)?;
17507        normalize_cpp_whitespace(node_text(identifier, source))
17508    };
17509    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17510        return None;
17511    }
17512    // The mis-lexed `namespace` keyword: an `ERROR` holding it under the older
17513    // grammar, or the function's bogus type field under the macro-declaration
17514    // grammar, where a keyword cannot be a type.
17515    let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17516        inner_error.named_child(0)?
17517    } else if inner_error.kind() == "type_identifier" {
17518        *inner_error
17519    } else {
17520        return None;
17521    };
17522    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17523        return None;
17524    }
17525    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17526        return None;
17527    }
17528    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17529    if inner_name.is_empty() || body.kind() != "compound_statement" {
17530        return None;
17531    }
17532    namespace_components.push(inner_name);
17533
17534    let mut cursor = body.walk();
17535    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17536        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17537            cpp_body_node(class_node).is_some()
17538                && class_like_name(class_node, source, ancestry)
17539                    .is_some_and(|name| !name.is_empty())
17540        })
17541    });
17542    if !has_complete_class
17543        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17544    {
17545        return None;
17546    }
17547
17548    Some(CppNestedNamespaceSentinel {
17549        function,
17550        body: *body,
17551        namespace_components,
17552    })
17553}
17554
17555/// Recognize a namespace-begin sentinel directly beneath the translation unit.
17556///
17557/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
17558/// function whose type is the sentinel, whose declarator is the structured
17559/// qualified name `namespace::a::b`, and whose body contains the namespace
17560/// items. Declaration indexing already reparses this bounded region. The
17561/// inverse scanner retains the original tree, so recover the same namespace
17562/// components from the declarator fields for its lexical-scope metadata.
17563fn cpp_root_namespace_sentinel<'tree>(
17564    node: Node<'tree>,
17565    source: &str,
17566    ancestry: &ParentIndex<'tree>,
17567) -> Option<CppNestedNamespaceSentinel<'tree>> {
17568    if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17569        return None;
17570    }
17571    let body = node.child_by_field_name("body")?;
17572    let declarator = node.child_by_field_name("declarator")?;
17573    let mut cursor = node.walk();
17574    let named = node
17575        .named_children(&mut cursor)
17576        .filter(|child| child.kind() != "comment")
17577        .collect::<Vec<_>>();
17578    if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17579        return None;
17580    }
17581    // The sentinel is the bogus return `type` (`type_identifier`), with the
17582    // literal `namespace` keyword leading the qualified declarator -- or,
17583    // under the macro-declaration grammar, a `macro_modifier`, with the
17584    // keyword occupying the type field (a keyword cannot be a type, which is
17585    // the same recovery evidence) and the namespace path as the declarator.
17586    let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17587        [first, second, _] => {
17588            let first_type = node.child_by_field_name("type")?;
17589            if !same_node(*first, first_type)
17590                || first_type.kind() != "type_identifier"
17591                || !same_node(*second, declarator)
17592                || !node.has_error()
17593            {
17594                return None;
17595            }
17596            (
17597                normalize_cpp_whitespace(node_text(first_type, source)),
17598                true,
17599            )
17600        }
17601        [first, second, third, _] => {
17602            let type_node = node.child_by_field_name("type")?;
17603            let identifier = macro_modifier_identifier(*first)?;
17604            if !same_node(*second, type_node)
17605                || type_node.kind() != "type_identifier"
17606                || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17607                || !same_node(*third, declarator)
17608            {
17609                return None;
17610            }
17611            (
17612                normalize_cpp_whitespace(node_text(identifier, source)),
17613                false,
17614            )
17615        }
17616        _ => return None,
17617    };
17618    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17619        return None;
17620    }
17621    if !matches!(
17622        declarator.kind(),
17623        "qualified_identifier" | "identifier" | "namespace_identifier"
17624    ) {
17625        return None;
17626    }
17627    let mut declarator_components = Vec::new();
17628    let mut valid_components = true;
17629    walk_named_tree_preorder(declarator, true, |component| {
17630        if !matches!(
17631            component.kind(),
17632            "identifier" | "namespace_identifier" | "type_identifier"
17633        ) {
17634            return WalkControl::Continue;
17635        }
17636        let Some(component) = canonical_cpp_qualified_component(component, source) else {
17637            valid_components = false;
17638            return WalkControl::Break;
17639        };
17640        declarator_components.push(component.name);
17641        WalkControl::SkipChildren
17642    });
17643    if strip_namespace_keyword {
17644        if !valid_components
17645            || declarator_components.first().map(String::as_str) != Some("namespace")
17646        {
17647            return None;
17648        }
17649        declarator_components.remove(0);
17650    }
17651    let namespace_components = declarator_components;
17652    if !valid_components
17653        || namespace_components.is_empty()
17654        || namespace_components
17655            .iter()
17656            .any(|component| component.is_empty() || cpp_export_macro_token(component))
17657    {
17658        return None;
17659    }
17660
17661    let mut cursor = body.walk();
17662    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17663        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17664            cpp_body_node(class_node).is_some()
17665                && class_like_name(class_node, source, ancestry)
17666                    .is_some_and(|name| !name.is_empty())
17667        })
17668    });
17669    if !has_complete_class
17670        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17671    {
17672        return None;
17673    }
17674
17675    Some(CppNestedNamespaceSentinel {
17676        function: node,
17677        body,
17678        namespace_components,
17679    })
17680}
17681
17682/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
17683/// malformed namespace-sentinel function.  The recovery is deliberately
17684/// structural: the class must be a direct body item, its own class node must be
17685/// erroneous and end before a unique anonymous `}` in the enclosing
17686/// declaration-list, and that namespace's next sibling must be a standalone
17687/// `;`.  The complete interior must pass the existing member-shaped reparse
17688/// gate. This avoids source brace scans and does not borrow a close from an
17689/// unrelated later declaration.
17690fn cpp_sentinel_fragmented_class_tail<'tree>(
17691    function: Node<'tree>,
17692    body: Node<'tree>,
17693    source: &str,
17694    ancestry: &ParentIndex<'tree>,
17695) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17696    let mut cursor = body.walk();
17697    let candidates = body
17698        .named_children(&mut cursor)
17699        .filter_map(|child| {
17700            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17701                let class_body = cpp_body_node(class_node)?;
17702                if !class_node.has_error() {
17703                    return None;
17704                }
17705                let name = class_like_name(class_node, source, ancestry)?;
17706                let raw_supertypes =
17707                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17708                        .then(|| extract_cpp_supertypes(class_node, source));
17709                return Some((
17710                    class_node,
17711                    template_node,
17712                    name,
17713                    class_body,
17714                    class_body.start_byte().checked_add(1)?,
17715                    raw_supertypes,
17716                    class_body,
17717                ));
17718            }
17719            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17720            Some((
17721                child,
17722                None,
17723                prefix.name,
17724                prefix.open,
17725                prefix.open.end_byte(),
17726                prefix.raw_supertypes,
17727                child,
17728            ))
17729        })
17730        .collect::<Vec<_>>();
17731    let [
17732        (
17733            class_node,
17734            template_node,
17735            name,
17736            class_body,
17737            reparse_start,
17738            raw_supertypes,
17739            member_container,
17740        ),
17741    ] = candidates.as_slice()
17742    else {
17743        return None;
17744    };
17745    if name.is_empty() {
17746        return None;
17747    }
17748
17749    let (close, semicolon) =
17750        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17751
17752    let reparse_end = close.start_byte();
17753    if *reparse_start >= reparse_end {
17754        return None;
17755    }
17756    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17757    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17758        return None;
17759    }
17760    let class_range = Range {
17761        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17762        end_byte: semicolon.end_byte(),
17763        start_line: template_node.map_or(class_node.start_position().row, |node| {
17764            node.start_position().row
17765        }) + 1,
17766        end_line: semicolon.end_position().row + 1,
17767    };
17768    Some(CppSentinelFragmentedClassTail {
17769        class_node: *class_node,
17770        template_node: *template_node,
17771        name: name.clone(),
17772        raw_supertypes: raw_supertypes.clone(),
17773        fragmented: FragmentedExportBody {
17774            reparse_start: *reparse_start,
17775            reparse_end,
17776            class_range,
17777        },
17778        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17779        member_container: *member_container,
17780    })
17781}
17782
17783/// The alias declarations a fragmented sentinel class still holds as
17784/// structured direct members in the original tree (#6902).
17785///
17786/// The member reparse cannot keep them: after an access label it reads
17787/// `using value_type = int;` in statement context, as a `using` variable
17788/// named `value_type`, and loses the target. The original node keeps the
17789/// alias kind, the target type, the exact source range, and the template and
17790/// preprocessor context. Ownership is proven by position alone: a direct
17791/// child of the class's member container, inside the interior the member
17792/// reparse covers, or inside a preprocessor conditional that is one. An alias
17793/// nested anywhere else is not admitted here.
17794fn cpp_sentinel_fragmented_class_aliases<'tree>(
17795    tail: &CppSentinelFragmentedClassTail<'tree>,
17796) -> Vec<Node<'tree>> {
17797    let mut aliases = Vec::new();
17798    let mut containers = vec![tail.member_container];
17799    while let Some(container) = containers.pop() {
17800        let mut cursor = container.walk();
17801        for child in container.named_children(&mut cursor) {
17802            if child.start_byte() < tail.fragmented.reparse_start
17803                || child.end_byte() > tail.fragmented.reparse_end
17804            {
17805                continue;
17806            }
17807            match child.kind() {
17808                // A conditional member keeps its owner; its guard comes from
17809                // this same original ancestry.
17810                "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17811                | "preproc_elifdef" => containers.push(child),
17812                "alias_declaration" if !child.has_error() => aliases.push(child),
17813                "template_declaration"
17814                    if !child.has_error()
17815                        && child
17816                            .named_children(&mut child.walk())
17817                            .any(|declaration| declaration.kind() == "alias_declaration") =>
17818                {
17819                    aliases.push(child)
17820                }
17821                _ => {}
17822            }
17823        }
17824    }
17825    aliases
17826}
17827
17828/// Recover the class and out-of-line owner scopes from every malformed
17829/// namespace-sentinel region in `root`.
17830///
17831/// This is the shared structural counterpart to
17832/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
17833/// reuses the visitor's sentinel/class admission predicates instead of parsing
17834/// source text a second time.  The returned values own only ranges and names, so
17835/// they can be retained by an inverted usage scan after the tree borrow ends.
17836pub fn cpp_sentinel_recovered_classes(
17837    root: Node<'_>,
17838    source: &str,
17839) -> Vec<CppSentinelRecoveredClass> {
17840    // The macro-declaration grammar absorbs the sentinel damage into structured
17841    // nodes and can leave the whole tree clean: `macro_modifier` and its kin are
17842    // admitted only where the older grammar would have reported an error, so a
17843    // clean tree carrying them holds the same recovery evidence. A tree with
17844    // neither errors nor macro-declaration nodes has no sentinel region.
17845    if !root.has_error()
17846        && !subtree_contains(root, |node| {
17847            matches!(
17848                node.kind(),
17849                "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
17850            )
17851        })
17852    {
17853        return Vec::new();
17854    }
17855    // This scan owns its walk of `root`, so it owns the parent index that walk
17856    // asks its ancestor questions through. Built after the error gate: a clean
17857    // tree without macro-declaration evidence returns without paying for one.
17858    let ancestry = ParentIndex::new(root);
17859    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
17860    let mut stack = vec![root];
17861    while let Some(current) = stack.pop() {
17862        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
17863            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
17864        {
17865            let namespace_components = cpp_sentinel_recovered_namespace_components(
17866                recovered.function,
17867                &recovered.namespace_components,
17868                source,
17869            );
17870            let fragmented = cpp_sentinel_fragmented_class_tail(
17871                recovered.function,
17872                recovered.body,
17873                source,
17874                &ancestry,
17875            );
17876            let mut class_candidates = Vec::new();
17877            let mut cursor = recovered.body.walk();
17878            for (class_node, template_node) in recovered
17879                .body
17880                .named_children(&mut cursor)
17881                .filter_map(cpp_sentinel_body_class_candidate)
17882            {
17883                let Some(name) = class_like_name(class_node, source, &ancestry) else {
17884                    continue;
17885                };
17886                if name.is_empty() {
17887                    continue;
17888                }
17889                let is_fragmented = fragmented
17890                    .as_ref()
17891                    .is_some_and(|tail| same_node(tail.class_node, class_node));
17892                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
17893                    continue;
17894                }
17895                let class_range = if is_fragmented {
17896                    fragmented
17897                        .as_ref()
17898                        .map(|tail| tail.fragmented.class_range)
17899                        .expect("fragmented class range is present when class matches")
17900                } else {
17901                    cpp_declaration_range(template_node.unwrap_or(class_node))
17902                };
17903                class_candidates.push((class_range, name));
17904            }
17905            if let Some(fragmented) = fragmented
17906                .as_ref()
17907                .filter(|tail| tail.class_node.kind() == "ERROR")
17908            {
17909                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
17910            }
17911
17912            let mut owner_ranges =
17913                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
17914            cpp_sentinel_extend_unique_owner_ranges(
17915                &mut owner_ranges,
17916                cpp_sentinel_recovered_sibling_owner_ranges(
17917                    recovered.function,
17918                    &namespace_components,
17919                    source,
17920                ),
17921            );
17922            for (class_range, name) in class_candidates {
17923                push_cpp_sentinel_recovered_class(
17924                    &mut recovered_classes,
17925                    cpp_declaration_range(recovered.body),
17926                    &namespace_components,
17927                    class_range,
17928                    name,
17929                    &owner_ranges,
17930                );
17931            }
17932
17933            if let Some(declaration_list) = recovered
17934                .function
17935                .parent()
17936                .filter(|parent| parent.kind() == "declaration_list")
17937            {
17938                let outer_namespace =
17939                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
17940                push_cpp_sentinel_sibling_classes(
17941                    &mut recovered_classes,
17942                    declaration_list,
17943                    recovered.function,
17944                    &outer_namespace,
17945                    source,
17946                    &ancestry,
17947                );
17948            }
17949        } else if let Some(region) =
17950            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
17951        {
17952            let namespace_components = cpp_sentinel_recovered_namespace_components(
17953                current,
17954                &region.namespace_components,
17955                source,
17956            );
17957            let owner_container = current
17958                .parent()
17959                .filter(|parent| parent.kind() == "declaration_list")
17960                .unwrap_or(current);
17961            let owner_ranges =
17962                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
17963            push_cpp_sentinel_recovered_class(
17964                &mut recovered_classes,
17965                cpp_declaration_range(owner_container),
17966                &namespace_components,
17967                Range {
17968                    start_byte: region.class_start,
17969                    end_byte: region.class_close_end,
17970                    start_line: region.class_start_line,
17971                    end_line: region.class_close_line,
17972                },
17973                region.name,
17974                &owner_ranges,
17975            );
17976        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
17977            // A generic sentinel-prefixed class can be reduced as a malformed
17978            // function/ERROR without the explicit `namespace X` token pair.
17979            // Reuse the declaration visitor's bounded reparse and retain only
17980            // the recovered class identity/range here.
17981            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
17982                region;
17983            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17984                continue;
17985            };
17986            let root = tree.root_node();
17987            let template_node = cpp_sentinel_reparsed_leading_template(root);
17988            // A region reparse is its own tree and needs its own parent index.
17989            let reparsed_ancestry = ParentIndex::new(root);
17990            let Some(reparsed_class) =
17991                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
17992            else {
17993                continue;
17994            };
17995            let class_node = reparsed_class.declaration_node;
17996            let name = reparsed_class.name;
17997            let namespace_components =
17998                cpp_sentinel_recovered_namespace_components(current, &[], source);
17999            let owner_container = current
18000                .parent()
18001                .filter(|parent| parent.kind() == "declaration_list")
18002                .unwrap_or(current);
18003            let mut owner_ranges =
18004                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18005            cpp_sentinel_extend_unique_owner_ranges(
18006                &mut owner_ranges,
18007                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18008            );
18009            push_cpp_sentinel_recovered_class(
18010                &mut recovered_classes,
18011                cpp_declaration_range(owner_container),
18012                &namespace_components,
18013                Range {
18014                    start_byte: class_start,
18015                    end_byte: close_end,
18016                    start_line: class_node.start_position().row + 1,
18017                    end_line: class_node.end_position().row + 1,
18018                },
18019                name,
18020                &owner_ranges,
18021            );
18022            if owner_container.kind() == "declaration_list" {
18023                push_cpp_sentinel_sibling_classes(
18024                    &mut recovered_classes,
18025                    owner_container,
18026                    current,
18027                    &namespace_components,
18028                    source,
18029                    &ancestry,
18030                );
18031            }
18032        }
18033
18034        let mut cursor = current.walk();
18035        stack.extend(current.named_children(&mut cursor));
18036    }
18037    // A shallower sentinel can expose nested classes as apparent namespace
18038    // siblings even after a deeper sentinel proves that a containing class
18039    // owns their ranges. Drop those shadow descriptors; scope recovery starts
18040    // from the proven containing class and appends parser-visible class
18041    // ancestors, preserving the full `Outer::Inner` chain.
18042    let shadowed = recovered_classes
18043        .iter()
18044        .map(|candidate| {
18045            recovered_classes.iter().any(|container| {
18046                container.class_range.start_byte <= candidate.class_range.start_byte
18047                    && container.class_range.end_byte >= candidate.class_range.end_byte
18048                    && container.class_range != candidate.class_range
18049                    && container.namespace_scope_components.len()
18050                        > candidate.namespace_scope_components.len()
18051                    && container
18052                        .namespace_scope_components
18053                        .starts_with(&candidate.namespace_scope_components)
18054            })
18055        })
18056        .collect::<Vec<_>>();
18057    let mut index = 0usize;
18058    recovered_classes.retain(|_| {
18059        let keep = !shadowed[index];
18060        index += 1;
18061        keep
18062    });
18063    recovered_classes
18064}
18065
18066/// A flat sentinel can swallow the first class while leaving later classes and
18067/// their out-of-line definitions as ordinary declaration-list siblings.  Once
18068/// the malformed class proves the sentinel envelope, retain those structurally
18069/// complete sibling classes under the same surviving namespace so every member
18070/// owner in the region uses one recovery contract.
18071fn push_cpp_sentinel_sibling_classes<'tree>(
18072    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18073    declaration_list: Node<'tree>,
18074    sentinel_node: Node<'tree>,
18075    namespace_components: &[String],
18076    source: &str,
18077    ancestry: &ParentIndex<'tree>,
18078) {
18079    let owner_ranges =
18080        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18081    let namespace_range = cpp_declaration_range(declaration_list);
18082    let mut cursor = declaration_list.walk();
18083    for (class_node, template_node) in declaration_list
18084        .named_children(&mut cursor)
18085        .filter(|child| !same_node(*child, sentinel_node))
18086        .filter_map(cpp_sentinel_body_class_candidate)
18087    {
18088        let Some(name) = class_like_name(class_node, source, ancestry) else {
18089            continue;
18090        };
18091        if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18092            continue;
18093        }
18094        push_cpp_sentinel_recovered_class(
18095            recovered_classes,
18096            namespace_range,
18097            namespace_components,
18098            cpp_declaration_range(template_node.unwrap_or(class_node)),
18099            name,
18100            &owner_ranges,
18101        );
18102    }
18103}
18104
18105fn push_cpp_sentinel_recovered_class(
18106    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18107    namespace_range: Range,
18108    namespace_components: &[String],
18109    class_range: Range,
18110    name: String,
18111    owner_ranges: &[CppSentinelRecoveredOwner],
18112) {
18113    let mut scope_components = namespace_components.to_vec();
18114    scope_components.push(name);
18115    let owner_ranges = owner_ranges
18116        .iter()
18117        .filter(|owner| owner.scope_components.starts_with(&scope_components))
18118        .cloned()
18119        .collect::<Vec<_>>();
18120    if recovered_classes.iter().any(|existing| {
18121        existing.class_range == class_range && existing.scope_components == scope_components
18122    }) {
18123        return;
18124    }
18125    recovered_classes.push(CppSentinelRecoveredClass {
18126        namespace_range,
18127        namespace_scope_components: namespace_components.to_vec(),
18128        class_range,
18129        scope_components,
18130        owner_ranges,
18131    });
18132}
18133
18134fn cpp_sentinel_recovered_namespace_components(
18135    function: Node<'_>,
18136    recovered_components: &[String],
18137    source: &str,
18138) -> Vec<String> {
18139    let mut ancestor_components = Vec::new();
18140    let mut ancestor = function.parent();
18141    while let Some(current) = ancestor {
18142        if current.kind() == "namespace_definition"
18143            && let Some(name_node) = current.child_by_field_name("name")
18144            && let Some(components) = cpp_name_components(name_node, source)
18145        {
18146            ancestor_components.push(
18147                components
18148                    .into_iter()
18149                    .map(|component| component.name)
18150                    .collect::<Vec<_>>(),
18151            );
18152        }
18153        ancestor = current.parent();
18154    }
18155    ancestor_components.reverse();
18156    let mut ancestors = ancestor_components
18157        .into_iter()
18158        .flatten()
18159        .collect::<Vec<_>>();
18160
18161    let overlap = (0..=ancestors.len().min(recovered_components.len()))
18162        .rev()
18163        .find(|length| {
18164            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18165        })
18166        .unwrap_or(0);
18167    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18168    ancestors
18169}
18170
18171fn cpp_sentinel_recovered_owner_ranges(
18172    body: Node<'_>,
18173    namespace_components: &[String],
18174    source: &str,
18175) -> Vec<CppSentinelRecoveredOwner> {
18176    let mut owners = Vec::new();
18177    walk_named_tree_preorder(body, true, |node| {
18178        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18179    });
18180    owners
18181}
18182
18183fn cpp_sentinel_collect_owner_range(
18184    node: Node<'_>,
18185    namespace_components: &[String],
18186    source: &str,
18187    owners: &mut Vec<CppSentinelRecoveredOwner>,
18188) -> WalkControl {
18189    if node.kind() != "function_definition" {
18190        return WalkControl::Continue;
18191    }
18192    let Some(function_declarator) = extract_function_declarator(node) else {
18193        return WalkControl::Continue;
18194    };
18195    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18196        return WalkControl::Continue;
18197    };
18198    let Some(mut components) = cpp_name_components(name_node, source) else {
18199        return WalkControl::Continue;
18200    };
18201    if components.len() <= 1 {
18202        return WalkControl::Continue;
18203    }
18204    components.pop();
18205    let mut owner_components = components
18206        .into_iter()
18207        .map(|component| component.name)
18208        .collect::<Vec<_>>();
18209    let overlap = (0..=namespace_components.len().min(owner_components.len()))
18210        .rev()
18211        .find(|length| {
18212            owner_components[..*length]
18213                == namespace_components[namespace_components.len().saturating_sub(*length)..]
18214        })
18215        .unwrap_or(0);
18216    let mut scope_components = namespace_components.to_vec();
18217    scope_components.extend(owner_components.drain(overlap..));
18218    if scope_components.len() <= namespace_components.len() {
18219        return WalkControl::Continue;
18220    }
18221    let range = cpp_declaration_range(node);
18222    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18223        existing.range == range && existing.scope_components == scope_components
18224    }) {
18225        owners.push(CppSentinelRecoveredOwner {
18226            range,
18227            owner_name_start_byte: name_node.start_byte(),
18228            namespace_component_count: namespace_components.len(),
18229            scope_components,
18230        });
18231    }
18232    WalkControl::Continue
18233}
18234
18235fn cpp_sentinel_extend_unique_owner_ranges(
18236    owners: &mut Vec<CppSentinelRecoveredOwner>,
18237    additional: Vec<CppSentinelRecoveredOwner>,
18238) {
18239    for owner in additional {
18240        if !owners.iter().any(|existing| {
18241            existing.range == owner.range && existing.scope_components == owner.scope_components
18242        }) {
18243            owners.push(owner);
18244        }
18245    }
18246}
18247
18248fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18249    if node.kind() != "ERROR" || node.named_child_count() != 1 {
18250        return false;
18251    }
18252    let Some(end_name) = node.named_child(0) else {
18253        return false;
18254    };
18255    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18256        return false;
18257    }
18258    let mut cursor = node.walk();
18259    node.children(&mut cursor)
18260        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18261}
18262
18263/// Collect owner definitions that the malformed sentinel left as later
18264/// declaration-list siblings. Parser-visible namespace siblings are a hard
18265/// boundary: their declarations must keep their own lexical namespace.
18266fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18267    parent: Node<'_>,
18268    sentinel_node: Node<'_>,
18269    namespace_components: &[String],
18270    source: &str,
18271) -> Vec<CppSentinelRecoveredOwner> {
18272    let mut owners = Vec::new();
18273    let mut after_sentinel = false;
18274    let mut cursor = parent.walk();
18275    for child in parent.named_children(&mut cursor) {
18276        if !after_sentinel {
18277            if same_node(child, sentinel_node) {
18278                after_sentinel = true;
18279            }
18280            continue;
18281        }
18282        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18283    }
18284    owners
18285}
18286
18287/// Collect owner definitions after a malformed namespace, stopping only at
18288/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
18289/// enclosing container is not trusted to belong to the recovered namespace.
18290fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18291    parent: Node<'_>,
18292    sentinel_node: Node<'_>,
18293    namespace_components: &[String],
18294    source: &str,
18295) -> Option<Vec<CppSentinelRecoveredOwner>> {
18296    let mut owners = Vec::new();
18297    let mut after_namespace = false;
18298    let mut cursor = parent.walk();
18299    for child in parent.named_children(&mut cursor) {
18300        if !after_namespace {
18301            if same_node(child, sentinel_node) {
18302                after_namespace = true;
18303            }
18304            continue;
18305        }
18306        if cpp_sentinel_namespace_end(child, source) {
18307            return Some(owners);
18308        }
18309        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18310    }
18311    None
18312}
18313
18314fn cpp_sentinel_append_owner_ranges(
18315    sibling: Node<'_>,
18316    namespace_components: &[String],
18317    source: &str,
18318    owners: &mut Vec<CppSentinelRecoveredOwner>,
18319) {
18320    walk_named_tree_preorder(sibling, true, |node| {
18321        if node.kind() == "namespace_definition" {
18322            return WalkControl::SkipChildren;
18323        }
18324        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18325    });
18326}
18327
18328fn cpp_sentinel_recovered_sibling_owner_ranges(
18329    sentinel_node: Node<'_>,
18330    namespace_components: &[String],
18331    source: &str,
18332) -> Vec<CppSentinelRecoveredOwner> {
18333    let Some(declaration_list) = sentinel_node
18334        .parent()
18335        .filter(|parent| parent.kind() == "declaration_list")
18336    else {
18337        return Vec::new();
18338    };
18339    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18340        declaration_list,
18341        sentinel_node,
18342        namespace_components,
18343        source,
18344    );
18345
18346    let Some(namespace) = declaration_list
18347        .parent()
18348        .filter(|parent| parent.kind() == "namespace_definition")
18349    else {
18350        return owners;
18351    };
18352    let Some(outer_parent) = namespace.parent() else {
18353        return owners;
18354    };
18355    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18356        outer_parent,
18357        namespace,
18358        namespace_components,
18359        source,
18360    ) {
18361        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18362    }
18363    owners
18364}
18365
18366fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18367    let mut current = function_declarator.child_by_field_name("declarator")?;
18368    loop {
18369        if let Some(name) = macro_decorated_unqualified_name(current) {
18370            current = name;
18371            continue;
18372        }
18373        if matches!(
18374            current.kind(),
18375            "qualified_identifier"
18376                | "scoped_identifier"
18377                | "scoped_type_identifier"
18378                | "identifier"
18379                | "field_identifier"
18380                | "operator_name"
18381                | "destructor_name"
18382                | "literal_operator_name"
18383        ) {
18384            return Some(current);
18385        }
18386        current = current
18387            .child_by_field_name("declarator")
18388            .or_else(|| current.child_by_field_name("name"))
18389            .or_else(|| last_named_child(current))?;
18390    }
18391}
18392
18393/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
18394/// `::` between the halves. The scope is an attribute-like macro token, not
18395/// a namespace; `Name` is the declared name.
18396///
18397/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
18398/// tree-sitter joins the attribute macro and the constructor name into one
18399/// `qualified_identifier` and marks the separator it had to invent as MISSING.
18400/// That flag is the grammar's own record that no separator is in the source,
18401/// so read it instead of looking for `::` in the node text. A genuine
18402/// `Outer::Inner` keeps a present separator and is declined here, and so is
18403/// the explicit-global `::name`, whose `::` is present and whose scope is
18404/// absent.
18405fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18406    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18407        return None;
18408    }
18409    let mut cursor = node.walk();
18410    if node
18411        .children(&mut cursor)
18412        .any(|child| child.kind() == "::" && !child.is_missing())
18413    {
18414        return None;
18415    }
18416    node.child_by_field_name("name")
18417}
18418
18419fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18420    enum Component<'tree> {
18421        Node(Node<'tree>),
18422        Name(Node<'tree>),
18423    }
18424    let mut stack = vec![Component::Node(node)];
18425    let mut components = Vec::new();
18426    while let Some(component) = stack.pop() {
18427        let node = match component {
18428            Component::Name(node) => {
18429                components.push(canonical_cpp_qualified_component(
18430                    node.child_by_field_name("name")?,
18431                    source,
18432                )?);
18433                continue;
18434            }
18435            Component::Node(node) => node,
18436        };
18437        if let Some(name) = macro_decorated_unqualified_name(node) {
18438            stack.push(Component::Node(name));
18439            continue;
18440        }
18441        match node.kind() {
18442            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18443                stack.push(Component::Name(node));
18444                if let Some(scope) = node.child_by_field_name("scope") {
18445                    stack.push(Component::Node(scope));
18446                }
18447            }
18448            _ => components.push(canonical_cpp_qualified_component(node, source)?),
18449        }
18450    }
18451    Some(components)
18452}
18453
18454fn cpp_sentinel_fragment_boundary<'tree>(
18455    function: Node<'tree>,
18456    class_node: Node<'tree>,
18457    class_body: Node<'tree>,
18458    source: &str,
18459) -> Option<(Node<'tree>, Node<'tree>)> {
18460    let declaration_list = function.parent()?;
18461    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18462        return None;
18463    }
18464    let namespace = declaration_list.parent()?;
18465    if namespace.kind() != "namespace_definition"
18466        || namespace.child_by_field_name("body") != Some(declaration_list)
18467    {
18468        return None;
18469    }
18470    let mut cursor = declaration_list.walk();
18471    let closes = declaration_list
18472        .children(&mut cursor)
18473        .filter(|child| {
18474            !child.is_named()
18475                && child.kind() == "}"
18476                && child.start_byte() >= function.end_byte()
18477                && child.start_byte() > class_node.end_byte()
18478                && child.start_byte() > class_body.start_byte()
18479        })
18480        .collect::<Vec<_>>();
18481    let [close] = closes.as_slice() else {
18482        return None;
18483    };
18484    let semicolon = namespace.next_named_sibling()?;
18485    if !cpp_is_stray_semicolon(semicolon, source)
18486        || close.end_byte() != namespace.end_byte()
18487        || semicolon.start_byte() < namespace.end_byte()
18488    {
18489        return None;
18490    }
18491    Some((*close, semicolon))
18492}
18493
18494/// Detect the bogus declaration/function tree that tree-sitter recovers for a
18495/// region prefixed by an object-like macro sentinel the parser cannot see
18496/// (issue #941), and return the byte range `[start, end)` of the swallowed
18497/// declaration interior to reparse.
18498///
18499/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
18500/// `function_definition` whose first non-comment named child is the sentinel
18501/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
18502/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
18503/// the real items.
18504/// `start` is the end of the sentinel identifier -- everything after it is the
18505/// genuine source. `end` is the node's end, extended across any trailing empty
18506/// `;` statement the mis-parse displaced past the node (the class/struct closing
18507/// semicolon), so the reparse sees a complete, brace-balanced item.
18508///
18509/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
18510/// node (`has_error`), or -- under the macro-declaration grammar, which parses
18511/// the sentinel region without recovery -- carry the sentinel as a
18512/// `macro_modifier`, a production admitted only where the parse would
18513/// otherwise contain an error. Unknown annotation/export macros can make a
18514/// real callable error-recovered even though tree-sitter still preserves its
18515/// declarator, so a preserved callable is admitted only when a displaced class
18516/// keyword precedes that declarator. The clean-reparse-to-items gate in
18517/// `cpp_reparsed_items_are_indexable` is the final arbiter.
18518/// Return the reparse start and, when present, the structurally recovered class
18519/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
18520/// separately from the reparse start because an opaque template-declaration
18521/// macro may precede it.
18522fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18523    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18524        return None;
18525    }
18526    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
18527    // retain a valid function declarator despite the unknown export macro making
18528    // the outer node erroneous. Remember that declarator for the ordering gate
18529    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
18530    // class keyword precedes a spurious callable assembled from a later member.
18531    let mut declarator_cursor = node.walk();
18532    let preserved_callable = node
18533        .children_by_field_name("declarator", &mut declarator_cursor)
18534        .find_map(extract_function_declarator);
18535    // Leading documentation comments are attached to the malformed
18536    // `function_definition` as named children.  They are not part of the
18537    // sentinel prefix, so select the first non-comment child structurally
18538    // rather than requiring the sentinel to be child zero.  This is the shape
18539    // emitted for nlohmann/json's `basic_json`: its class documentation comment
18540    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
18541    // envelope otherwise ends at the first nested union.
18542    let mut cursor = node.walk();
18543    let first = node
18544        .named_children(&mut cursor)
18545        .find(|child| child.kind() != "comment")?;
18546    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
18547    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
18548    // the same token is wrapped in. The wrapper is itself the recovery
18549    // evidence the older grammar's `has_error` carried, so a clean envelope is
18550    // admitted only in that form. The macro-declaration grammar can also keep
18551    // the sentinel as the `type` and absorb the displaced head that follows it
18552    // (`BEGIN_NS TEMPLATE_DECLARATION class Box { ... }`) into a
18553    // `macro_modifier_declarator` chain, which carries the same evidence.
18554    let (first_end, sentinel) = if first.kind() == "type_identifier" {
18555        if !node.has_error()
18556            && node
18557                .child_by_field_name("declarator")
18558                .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18559        {
18560            return None;
18561        }
18562        (
18563            first.end_byte(),
18564            normalize_cpp_whitespace(node_text(first, source)),
18565        )
18566    } else {
18567        let identifier = macro_modifier_identifier(first)?;
18568        (
18569            first.end_byte(),
18570            normalize_cpp_whitespace(node_text(identifier, source)),
18571        )
18572    };
18573    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18574        return None;
18575    }
18576    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
18577    // makes the trailing sentinel of one region and the leading sentinel of the
18578    // next both land as bare macro-token identifiers ahead of the real content.
18579    // Advance past every leading macro-token identifier so the reparse begins at
18580    // genuine source rather than another sentinel that would re-form the bogus
18581    // shape and fail the reparse gate.
18582    let mut start = first_end;
18583    let mut after_first = false;
18584    let mut cursor = node.walk();
18585    for child in node.named_children(&mut cursor) {
18586        if !after_first {
18587            if same_node(child, first) {
18588                after_first = true;
18589            }
18590            continue;
18591        }
18592        let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18593            Some(child)
18594        } else {
18595            macro_modifier_identifier(child)
18596        };
18597        if macro_token.is_some_and(|token| {
18598            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18599        }) {
18600            start = child.end_byte();
18601        } else {
18602            break;
18603        }
18604    }
18605    // An additional opaque template-declaration macro before a class can be
18606    // folded into the bogus function's qualified declarator.  In that shape
18607    // the macro is not a direct sibling we can skip above; tree-sitter exposes
18608    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
18609    // Reparse from that keyword (or a real preceding `template` keyword) so the
18610    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
18611    // a class nested in a genuine sentinel-wrapped namespace lies after the
18612    // body opening and must not change the established region start.
18613    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18614    let mut class_start = None;
18615    let mut template_start = None;
18616    let mut stack = vec![node];
18617    while let Some(current) = stack.pop() {
18618        if current.start_byte() >= prefix_end {
18619            continue;
18620        }
18621        if matches!(
18622            current.kind(),
18623            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18624        ) {
18625            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18626                "class" | "struct" | "union" | "enum" => {
18627                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18628                        seen.min(current.start_byte())
18629                    }));
18630                }
18631                "template" => {
18632                    template_start =
18633                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
18634                            seen.min(current.start_byte())
18635                        }));
18636                }
18637                _ => {}
18638            }
18639        }
18640        let mut cursor = current.walk();
18641        stack.extend(current.children(&mut cursor));
18642    }
18643    if preserved_callable.is_some_and(|callable| {
18644        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18645    }) {
18646        return None;
18647    }
18648    if let Some(class_start) = class_start {
18649        start = template_start
18650            .filter(|template_start| *template_start < class_start)
18651            .unwrap_or(class_start);
18652    }
18653    Some((start, class_start))
18654}
18655
18656/// Locate a sentinel-prefixed class whose malformed declaration was split across
18657/// root-level siblings. The true class close is represented structurally as a
18658/// lone `}` error followed by the class's displaced `;`; nested method/body
18659/// errors are not direct siblings of the sentinel node and therefore cannot
18660/// satisfy this pair.
18661fn cpp_sentinel_macro_class_region<'tree>(
18662    node: Node<'tree>,
18663    source: &str,
18664) -> Option<(usize, usize, usize, usize, usize, usize)> {
18665    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18666        return None;
18667    };
18668    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18669        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18670        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18671    if class_start >= body_open_start {
18672        return None;
18673    }
18674    let sibling_close = {
18675        let mut sibling = node.next_named_sibling();
18676        let mut found = None;
18677        while let Some(current) = sibling {
18678            let next = current.next_named_sibling();
18679            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18680                found = Some((
18681                    current.start_byte(),
18682                    semicolon.end_byte(),
18683                    semicolon.end_position().row + 1,
18684                ));
18685                break;
18686            }
18687            sibling = next;
18688        }
18689        found
18690    };
18691    // A stray `};` sibling is this class's close only when the bounded reparse
18692    // agrees the first body-bearing class ENDS there. When the malformed
18693    // envelope swallowed the class's true close, the scan can promote a much
18694    // later scope's close instead -- in protobuf-generated headers
18695    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
18696    // `struct TableStruct_*` paired with the first message class's `};`, making
18697    // the recovered "class body" span whole `namespace {}` blocks and minting
18698    // namespace-scope classes as nested members of the recovered class, which
18699    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
18700    // (#2275). On disagreement, fall through to the suffix-reparse boundary
18701    // below, which derives the close from the class node's own balanced body
18702    // range.
18703    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18704        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18705            return false;
18706        };
18707        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18708        // A region reparse is its own tree and needs its own parent index.
18709        let reparsed_ancestry = ParentIndex::new(tree.root_node());
18710        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18711            tree.root_node(),
18712            template_node,
18713            source,
18714            &reparsed_ancestry,
18715        ) else {
18716            return false;
18717        };
18718        let body = reparsed_class.body;
18719        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18720    });
18721    let (class_close_start, class_close_end, class_close_line) =
18722        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18723            (class_close_start, class_close_end, class_close_line)
18724        } else {
18725            // When the malformed envelope itself is an ERROR, tree-sitter can
18726            // leave the class's balanced close in the source while promoting
18727            // all following members to siblings. Reparse the complete suffix
18728            // and use the first body-bearing class node's own field range as
18729            // the partition boundary. This keeps balancing in tree-sitter and
18730            // preserves the source's original byte offsets.
18731            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18732            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18733            // A region reparse is its own tree and needs its own parent index.
18734            let reparsed_ancestry = ParentIndex::new(tree.root_node());
18735            let reparsed_class = cpp_sentinel_reparsed_class(
18736                tree.root_node(),
18737                template_node,
18738                source,
18739                &reparsed_ancestry,
18740            )?;
18741            let body = reparsed_class.body;
18742            let class_close_end = body.end_byte();
18743            let class_close_start = class_close_end.checked_sub(1)?;
18744            let class_close_line = body.end_position().row + 1;
18745            (class_close_start, class_close_end, class_close_line)
18746        };
18747    if class_close_start <= class_start {
18748        return None;
18749    }
18750
18751    // Reparse only far enough to expose the class body opening. This is a
18752    // structured check that the candidate really begins with a body-bearing
18753    // class-like item; the original malformed tree cannot provide that node.
18754    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18755    let class_root = tree.root_node();
18756    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18757    // A region reparse is its own tree and needs its own parent index.
18758    let reparsed_ancestry = ParentIndex::new(class_root);
18759    let reparsed_class =
18760        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18761    let body = reparsed_class.body;
18762    // The class body opening must agree with the malformed wrapper's structured
18763    // body field. This rejects an inner nested class while permitting later
18764    // members to remain fragmented as root-level siblings in the bounded parse.
18765    if body.start_byte() != body_open_start {
18766        return None;
18767    }
18768    let body_start = body.start_byte().checked_add(1)?;
18769    (body_start < class_close_start).then_some((
18770        reparse_start,
18771        class_start,
18772        body_start,
18773        class_close_start,
18774        class_close_end,
18775        class_close_line,
18776    ))
18777}
18778
18779/// Find the `{` token immediately following the class/struct/union/enum token
18780/// at `class_start` in the malformed tree. The token is anonymous in the C++
18781/// grammar, so this deliberately walks all children (not only named children)
18782/// and relies on sibling structure rather than source-text searching.
18783fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18784    let mut stack = vec![node];
18785    while let Some(current) = stack.pop() {
18786        if current.start_byte() == class_start
18787            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18788        {
18789            // The macro-declaration grammar keeps the class whole: the token's
18790            // specifier ancestor owns the body, and its `{` is the body's
18791            // first child rather than a later sibling of the token.
18792            let mut ancestor = current.parent();
18793            while let Some(parent) = ancestor {
18794                if matches!(
18795                    parent.kind(),
18796                    "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18797                ) {
18798                    if let Some(body) = cpp_body_node(parent) {
18799                        return body
18800                            .child(0)
18801                            .filter(|open| open.kind() == "{")
18802                            .map(|open| open.start_byte());
18803                    }
18804                    break;
18805                }
18806                if matches!(parent.kind(), "ERROR" | "declaration") {
18807                    break;
18808                }
18809                ancestor = parent.parent();
18810            }
18811            let mut sibling = current.next_sibling();
18812            while let Some(candidate) = sibling {
18813                if candidate.kind() == "{" {
18814                    return Some(candidate.start_byte());
18815                }
18816                sibling = candidate.next_sibling();
18817            }
18818        }
18819        let mut cursor = current.walk();
18820        stack.extend(current.children(&mut cursor));
18821    }
18822    None
18823}
18824
18825/// The class body that tree-sitter displaced out of a sentinel-prefixed
18826/// declaration and left as the malformed node's next sibling.
18827///
18828/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
18829/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
18830/// the last child of that `ERROR` and its `{` opens a sibling
18831/// `compound_statement` instead. The body is still the malformed tree's own
18832/// structured token, which is what the caller's `body.start_byte() !=
18833/// body_open_start` agreement check needs; it just is not reachable by walking
18834/// forward from the class token inside the node.
18835fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18836    node.next_named_sibling()
18837        .filter(|sibling| sibling.kind() == "compound_statement")
18838}
18839
18840fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
18841    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
18842    let mut end = if class_start.is_some() {
18843        cpp_macro_prefixed_class_end(source, start)?
18844    } else {
18845        node.end_byte()
18846    };
18847    if class_start.is_none()
18848        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
18849    {
18850        end = end.max(namespace_end);
18851    }
18852    let mut sibling = node.next_named_sibling();
18853    while let Some(current) = sibling {
18854        if !cpp_is_stray_semicolon(current, source) {
18855            break;
18856        }
18857        end = current.end_byte();
18858        sibling = current.next_named_sibling();
18859    }
18860    (start < end).then_some((start, end))
18861}
18862
18863/// Extend a sentinel reparse through a following namespace that tree-sitter
18864/// flattened into the sentinel node's sibling list.
18865///
18866/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
18867/// unknown macro becomes a false function return type and consumes the first
18868/// namespace body. A second `namespace detail` then loses its enclosing node:
18869/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
18870/// attaches its declarations to the surrounding error tree. Reparse from that
18871/// structured keyword so tree-sitter, rather than a source-text brace scan,
18872/// supplies the complete namespace boundary.
18873fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
18874    let mut sibling = node.next_sibling();
18875    let keyword = loop {
18876        let candidate = sibling?;
18877        sibling = candidate.next_sibling();
18878        if candidate.kind() != "comment" {
18879            break candidate;
18880        }
18881    };
18882    if keyword.kind() != "namespace" {
18883        return None;
18884    }
18885    let name = loop {
18886        let candidate = sibling?;
18887        sibling = candidate.next_sibling();
18888        if candidate.kind() != "comment" {
18889            break candidate;
18890        }
18891    };
18892    if cpp_namespace_name_components(name, source).is_empty() {
18893        return None;
18894    }
18895    let open = loop {
18896        let candidate = sibling?;
18897        sibling = candidate.next_sibling();
18898        if candidate.kind() != "comment" {
18899            break candidate;
18900        }
18901    };
18902    if open.kind() != "{" {
18903        return None;
18904    }
18905
18906    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
18907    let root = tree.root_node();
18908    let mut cursor = root.walk();
18909    let namespace = root
18910        .named_children(&mut cursor)
18911        .find(|candidate| candidate.kind() != "comment")?;
18912    (namespace.kind() == "namespace_definition"
18913        && namespace.start_byte() == keyword.start_byte()
18914        && namespace.child_by_field_name("body").is_some())
18915    .then_some(namespace.end_byte())
18916}
18917
18918/// Parse the source suffix beginning at a structurally recovered class/template
18919/// keyword and return the end of its first body-bearing class item.  The parser,
18920/// rather than a brace scanner, owns nested-body balancing.  This is needed when
18921/// the original error tree truncates the class and scatters later members as
18922/// top-level siblings.
18923fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
18924    let tree = cpp_reparse_region_items(source, start, source.len())?;
18925    let root = tree.root_node();
18926    let mut cursor = root.walk();
18927    for item in root.named_children(&mut cursor) {
18928        if item.end_byte() <= start || item.kind() == "comment" {
18929            continue;
18930        }
18931        let mut stack = vec![item];
18932        while let Some(current) = stack.pop() {
18933            if matches!(
18934                current.kind(),
18935                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18936            ) && cpp_body_node(current).is_some()
18937            {
18938                return Some(current.end_byte());
18939            }
18940            let mut cursor = current.walk();
18941            stack.extend(current.named_children(&mut cursor));
18942        }
18943        // The recovered prefix is required to begin with the class item.  If
18944        // the first real item is something else, fail closed rather than skip
18945        // arbitrary source looking for a later class.
18946        return None;
18947    }
18948    None
18949}
18950
18951/// An empty `;` statement: the displaced closing semicolon of a struct/class that
18952/// the sentinel mis-parse split off past the bogus function node.
18953fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
18954    node.kind() == "expression_statement"
18955        && node.named_child_count() == 0
18956        && node_text(node, source).trim() == ";"
18957}
18958
18959fn cpp_stray_close_followed_by_semicolon<'tree>(
18960    close: Node<'tree>,
18961    next: Option<Node<'tree>>,
18962    source: &str,
18963) -> Option<Node<'tree>> {
18964    cpp_is_stray_close_brace(close, source)
18965        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
18966        .flatten()
18967}
18968
18969/// Recover the member that follows CPython's object header macro when
18970/// tree-sitter folds both declarations into one malformed field.
18971///
18972/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
18973/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
18974/// sole child is the real field declarator. The tail may wrap that identifier
18975/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
18976/// malformed wrappers do not provide the same evidence that this is the
18977/// CPython macro boundary.
18978#[derive(Clone, Copy)]
18979pub(crate) struct RecoveredPyObjectHeadField<'tree> {
18980    pub(crate) type_node: Node<'tree>,
18981    pub(crate) name: Node<'tree>,
18982    pub(crate) declarator: Node<'tree>,
18983}
18984
18985impl RecoveredPyObjectHeadField<'_> {
18986    pub(crate) fn pointer_depth(self) -> i32 {
18987        let mut depth = 0;
18988        let mut current = self.declarator;
18989        while current != self.name {
18990            debug_assert_eq!(current.kind(), "pointer_declarator");
18991            depth += 1;
18992            current = current
18993                .child_by_field_name("declarator")
18994                .expect("recovered PyObject field pointer has an inner declarator");
18995        }
18996        depth
18997    }
18998}
18999
19000pub(crate) fn recovered_pyobject_head_field<'tree>(
19001    node: Node<'tree>,
19002    source: &str,
19003) -> Option<RecoveredPyObjectHeadField<'tree>> {
19004    if node.kind() != "field_declaration" {
19005        return None;
19006    }
19007    let type_node = node.child_by_field_name("type")?;
19008    if type_node.kind() != "type_identifier"
19009        || node_text(type_node, source).trim() != "PyObject_HEAD"
19010    {
19011        return None;
19012    }
19013    let pseudo_declarator = node.child_by_field_name("declarator")?;
19014    let mut cursor = node.walk();
19015    let errors = node
19016        .named_children(&mut cursor)
19017        .filter(|child| child.kind() == "ERROR")
19018        .collect::<Vec<_>>();
19019    let [error] = errors.as_slice() else {
19020        return None;
19021    };
19022    if error.named_child_count() != 1 {
19023        return None;
19024    }
19025    let error_child = error.named_child(0)?;
19026    if pseudo_declarator.kind() == "field_identifier"
19027        && error.start_byte() >= pseudo_declarator.end_byte()
19028        && error_child.kind() == "identifier"
19029    {
19030        return Some(RecoveredPyObjectHeadField {
19031            type_node: pseudo_declarator,
19032            name: error_child,
19033            declarator: error_child,
19034        });
19035    }
19036    if pseudo_declarator.kind() != "pointer_declarator"
19037        || error.end_byte() > pseudo_declarator.start_byte()
19038        || error_child.kind() != "identifier"
19039    {
19040        return None;
19041    }
19042    let mut name = pseudo_declarator;
19043    while name.kind() == "pointer_declarator" {
19044        name = name.child_by_field_name("declarator")?;
19045    }
19046    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19047        type_node: error_child,
19048        name,
19049        declarator: pseudo_declarator,
19050    })
19051}
19052
19053/// Recover the real field name when a leading object-like annotation macro
19054/// displaces a qualified type into tree-sitter's bit-field recovery shape.
19055///
19056/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
19057/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
19058/// `bitfield_clause` containing an error plus an assignment.  The assignment's
19059/// left field is the only structured declaration name in that malformed tail.
19060/// A real bit-field is excluded by the all-caps macro type and required error.
19061fn recovered_macro_qualified_field_clause<'tree>(
19062    node: Node<'tree>,
19063    source: &str,
19064) -> Option<Node<'tree>> {
19065    if node.kind() != "field_declaration" {
19066        return None;
19067    }
19068    let macro_type = node.child_by_field_name("type")?;
19069    if macro_type.kind() != "type_identifier"
19070        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19071    {
19072        return None;
19073    }
19074    let mut cursor = node.walk();
19075    node.named_children(&mut cursor)
19076        .find(|child| child.kind() == "bitfield_clause")
19077}
19078
19079fn recovered_macro_qualified_field_declarators<'tree>(
19080    node: Node<'tree>,
19081    source: &str,
19082) -> Option<Vec<Node<'tree>>> {
19083    let pseudo_declarator = node.child_by_field_name("declarator")?;
19084    if pseudo_declarator.kind() != "field_identifier" {
19085        return None;
19086    }
19087    let clause = recovered_macro_qualified_field_clause(node, source)?;
19088    if !(0..clause.named_child_count()).any(|index| {
19089        clause
19090            .named_child(index)
19091            .is_some_and(|child| child.kind() == "ERROR")
19092    }) {
19093        return None;
19094    }
19095    let mut recovered = Vec::new();
19096    let mut stack = vec![clause];
19097    while let Some(current) = stack.pop() {
19098        if current.kind() == "assignment_expression"
19099            && let Some(left) = current.child_by_field_name("left")
19100            && extract_variable_name(left, source).is_some()
19101        {
19102            recovered.push(left);
19103            break;
19104        }
19105        let mut cursor = current.walk();
19106        stack.extend(current.named_children(&mut cursor));
19107    }
19108    if recovered.is_empty() {
19109        return None;
19110    }
19111    let mut cursor = node.walk();
19112    recovered.extend(
19113        node.children_by_field_name("declarator", &mut cursor)
19114            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19115    );
19116    Some(recovered)
19117}
19118
19119/// Recover a macro-qualified constructor that tree-sitter represents as one
19120/// field declaration. The constructor call remains inside the direct recovery
19121/// error, while each member initializer becomes a false function declarator.
19122/// The class owner proves the constructor name and lets the caller ignore those
19123/// initializer declarators.
19124fn recovered_macro_qualified_constructor_call<'tree>(
19125    node: Node<'tree>,
19126    class_name: &str,
19127    source: &str,
19128) -> Option<Node<'tree>> {
19129    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19130    let error = bitfield
19131        .named_child(0)
19132        .filter(|child| child.kind() == "ERROR")?;
19133    let mut stack = vec![error];
19134    while let Some(current) = stack.pop() {
19135        if current.kind() == "call_expression"
19136            && current
19137                .child_by_field_name("function")
19138                .is_some_and(|function| node_text(function, source) == class_name)
19139            && current
19140                .child_by_field_name("arguments")
19141                .is_some_and(|arguments| arguments.kind() == "argument_list")
19142        {
19143            return Some(current);
19144        }
19145        let mut cursor = current.walk();
19146        stack.extend(current.named_children(&mut cursor));
19147    }
19148    None
19149}
19150
19151/// The constructor head of a macro-decorated constructor that the
19152/// macro-declaration grammar reads as one field declaration
19153/// (`MSC_WARNING(suppress : 26495) CONSTEXPR Buffer(grow_fun grow) noexcept
19154/// : size_(size), grow_(grow) {}`, #1838). The macro is the field's `type`,
19155/// the constructor head is its first declarator (possibly inside a
19156/// `macro_modifier_declarator`), the first member initializer is a
19157/// `bitfield_clause` call, every later initializer is another function
19158/// declarator, and the body is the `default_value` initializer list. A
19159/// constructor declares no type, and no real field pairs a callable declarator
19160/// with a bitfield, so a head naming the enclosing class beside a bitfield
19161/// call is this shape. The caller publishes the head and none of the
19162/// initializer declarators.
19163fn recovered_macro_initialized_constructor_declarator<'tree>(
19164    node: Node<'tree>,
19165    class_name: &str,
19166    source: &str,
19167) -> Option<Node<'tree>> {
19168    if node.kind() != "field_declaration" {
19169        return None;
19170    }
19171    let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19172    if cpp_function_declarator_name_node(constructor)
19173        .is_none_or(|name| node_text(name, source) != class_name)
19174    {
19175        return None;
19176    }
19177    let mut cursor = node.walk();
19178    let initializer = node
19179        .named_children(&mut cursor)
19180        .find(|child| child.kind() == "bitfield_clause")?
19181        .named_child(0)?;
19182    (initializer.kind() == "call_expression").then_some(constructor)
19183}
19184
19185/// Recover a macro-qualified member function declaration that tree-sitter
19186/// represents as a pseudo-field. An object-like export macro before a qualified
19187/// return type can displace the namespace and type into an ERROR/bitfield
19188/// recovery, leaving the callable as a structured `call_expression`.
19189///
19190/// The caller must route this shape before ordinary declarator classification;
19191/// otherwise the displaced namespace identifier is published as a field.
19192fn recovered_macro_qualified_function_call<'tree>(
19193    node: Node<'tree>,
19194    source: &str,
19195) -> Option<Node<'tree>> {
19196    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19197    let declarator = node.child_by_field_name("declarator")?;
19198    if declarator.kind() != "field_identifier" {
19199        return None;
19200    }
19201    let mut cursor = node.walk();
19202    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19203    if !named.iter().any(|child| {
19204        child.kind() == "storage_class_specifier"
19205            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19206    }) {
19207        return None;
19208    }
19209    let mut bitfield_cursor = bitfield.walk();
19210    let payload = bitfield
19211        .named_children(&mut bitfield_cursor)
19212        .collect::<Vec<_>>();
19213    let [displaced_error, call] = payload.as_slice() else {
19214        return None;
19215    };
19216    if displaced_error.kind() != "ERROR"
19217        || displaced_error.named_child_count() != 1
19218        || displaced_error
19219            .named_child(0)
19220            .is_none_or(|child| child.kind() != "identifier")
19221        || call.kind() != "call_expression"
19222        || call
19223            .child_by_field_name("function")
19224            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19225        || call
19226            .child_by_field_name("arguments")
19227            .is_none_or(|arguments| arguments.kind() != "argument_list")
19228    {
19229        return None;
19230    }
19231    Some(*call)
19232}
19233
19234/// The real constructor declarator of a macro-decorated constructor the
19235/// macro-declaration grammar reads as an explicit specifier, an `ERROR`
19236/// holding the macro and the constructor head as a `macro_modifier_declarator`,
19237/// and the member initializer mis-read as the function's own declarator
19238/// (`explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}`, #2552 shape 1).
19239/// The constructor head inside the `ERROR` is what names the class; the
19240/// declarator the `function_definition` fields is the initializer, never the
19241/// member.
19242fn recovered_macro_decorated_constructor_declarator<'tree>(
19243    node: Node<'tree>,
19244    class_name: &str,
19245    source: &str,
19246) -> Option<Node<'tree>> {
19247    if node.kind() != "function_definition" {
19248        return None;
19249    }
19250    let mut cursor = node.walk();
19251    let mut heads = Vec::new();
19252    for error in node
19253        .named_children(&mut cursor)
19254        .filter(|child| child.kind() == "ERROR")
19255    {
19256        let mut stack = vec![error];
19257        while let Some(current) = stack.pop() {
19258            let is_constructor_head = match current.kind() {
19259                "macro_modifier_declarator" | "function_declarator" => {
19260                    extract_function_declarator(current).is_some_and(|declarator| {
19261                        cpp_function_declarator_name_node(declarator)
19262                            .is_some_and(|name| node_text(name, source) == class_name)
19263                    })
19264                }
19265                _ => false,
19266            };
19267            if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19268                heads.push(declarator);
19269                continue;
19270            }
19271            let mut cursor = current.walk();
19272            stack.extend(current.named_children(&mut cursor));
19273        }
19274    }
19275    let [head] = heads.as_slice() else {
19276        return None;
19277    };
19278    Some(*head)
19279}
19280
19281fn recovered_macro_qualified_function_parameters(
19282    arguments: Node<'_>,
19283    source: &str,
19284) -> Option<(String, Vec<String>)> {
19285    if arguments.kind() != "argument_list" {
19286        return None;
19287    }
19288    let mut cursor = arguments.walk();
19289    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19290    if named.is_empty() {
19291        return Some(("()".to_string(), Vec::new()));
19292    }
19293    let mut types = Vec::new();
19294    let mut labels = Vec::new();
19295    let mut index = 0;
19296    while index < named.len() {
19297        let parameter_type = named[index];
19298        let parameter_name = named.get(index + 1).copied()?;
19299        if !matches!(
19300            parameter_type.kind(),
19301            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19302        ) || parameter_name.kind() != "ERROR"
19303            || parameter_name.named_child_count() != 1
19304            || parameter_name
19305                .named_child(0)
19306                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19307        {
19308            return None;
19309        }
19310        let parameter_name = parameter_name.named_child(0)?;
19311        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19312        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19313        index += 2;
19314    }
19315    Some((format!("({})", types.join(", ")), labels))
19316}
19317
19318/// Recognize the phantom field tree-sitter emits for a macro-qualified
19319/// function return type.  For example,
19320/// `static API result_type ThresholdForSmallA() { ... }` can become a
19321/// `field_declaration` (`API` as the type and `result_type` as a field name)
19322/// followed by a clean `function_definition` for `ThresholdForSmallA`.
19323///
19324/// Keep this predicate entirely tied to the CST envelope: the type must be an
19325/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
19326/// the declaration must carry a missing semicolon rather than a real one, and
19327/// the immediate named sibling must expose a function declarator.  A real
19328/// macro-decorated field with an explicit semicolon therefore remains a field.
19329pub fn recovered_macro_return_type_node<'tree>(
19330    node: Node<'tree>,
19331    source: &str,
19332) -> Option<Node<'tree>> {
19333    if node.kind() != "field_declaration" {
19334        return None;
19335    }
19336    let macro_type = node.child_by_field_name("type")?;
19337    if macro_type.kind() != "type_identifier"
19338        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19339    {
19340        return None;
19341    }
19342    let declarator = node.child_by_field_name("declarator")?;
19343    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19344        return None;
19345    }
19346    let mut has_missing_semicolon = false;
19347    let mut has_real_semicolon = false;
19348    for child in children_iter(node) {
19349        if child.kind() != ";" {
19350            continue;
19351        }
19352        if child.is_missing() {
19353            has_missing_semicolon = true;
19354        } else {
19355            has_real_semicolon = true;
19356        }
19357    }
19358    if !has_missing_semicolon || has_real_semicolon {
19359        return None;
19360    }
19361    let mut next = node.next_named_sibling();
19362    while next.is_some_and(|sibling| sibling.kind() == "comment") {
19363        next = next.and_then(|sibling| sibling.next_named_sibling());
19364    }
19365    let next = next?;
19366    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19367        return None;
19368    }
19369    let function_declarator = next.child_by_field_name("declarator")?;
19370    extract_function_declarator(function_declarator).map(|_| declarator)
19371}
19372
19373/// The omitted-semicolon class field the macro-declaration grammar folds into
19374/// the head of the normally typed function that follows it. The older grammar
19375/// left `DWORD preserved_field` ahead of `void PreserveFollowingFunction() {}`
19376/// as a damaged `field_declaration`; the macro-declaration grammar reads the
19377/// same pair as two leading `macro_modifier`s of the clean function
19378/// definition. Only an exact modifier pair ahead of a real type field and a
19379/// function declarator is a field: a lone modifier ahead of the type is the
19380/// macro-return shape (`static API result_type f() { ... }`), which carries
19381/// no field.
19382fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19383    if node.kind() != "function_definition" {
19384        return None;
19385    }
19386    let type_node = node.child_by_field_name("type")?;
19387    let declarator = node.child_by_field_name("declarator")?;
19388    extract_function_declarator(declarator)?;
19389    let mut cursor = node.walk();
19390    let head = node
19391        .named_children(&mut cursor)
19392        .take_while(|child| *child != type_node)
19393        .filter(|child| child.kind() != "comment")
19394        .collect::<Vec<_>>();
19395    let [type_modifier, name_modifier] = head.as_slice() else {
19396        return None;
19397    };
19398    macro_modifier_identifier(*type_modifier)?;
19399    macro_modifier_identifier(*name_modifier)?;
19400    Some((*type_modifier, *name_modifier))
19401}
19402
19403/// The stand-in field evidence in a clean macro-return function definition:
19404/// `CUTLASS_DEVICE static OperandLayout Layout() { ... }` has a
19405/// `macro_modifier` head and a return type naming an active template type
19406/// parameter. The older grammar read the same head as a phantom
19407/// `field_declaration` named after the type, and dependent calls like
19408/// `OperandLayout::packed` resolved to that field (#1129). Returns the head's
19409/// first token and the type node; the active-parameter gate is the same one
19410/// the phantom-field suppression used, so a macro return type naming no
19411/// template parameter (`result_type`) recovers nothing.
19412fn macro_return_template_parameter_field<'tree>(
19413    node: Node<'tree>,
19414    source: &str,
19415    ancestry: &ParentIndex<'tree>,
19416) -> Option<(Node<'tree>, Node<'tree>)> {
19417    if node.kind() != "function_definition" {
19418        return None;
19419    }
19420    let type_node = node.child_by_field_name("type")?;
19421    if type_node.kind() != "type_identifier" {
19422        return None;
19423    }
19424    let declarator = node.child_by_field_name("declarator")?;
19425    extract_function_declarator(declarator)?;
19426    let mut cursor = node.walk();
19427    let head = node
19428        .named_children(&mut cursor)
19429        .take_while(|child| *child != type_node)
19430        .filter(|child| child.kind() != "comment")
19431        .collect::<Vec<_>>();
19432    let [first, ..] = head.as_slice() else {
19433        return None;
19434    };
19435    if !head
19436        .iter()
19437        .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19438        || !head.iter().any(|child| child.kind() == "macro_modifier")
19439    {
19440        return None;
19441    }
19442    let name = normalize_cpp_whitespace(node_text(type_node, source));
19443    if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19444        return None;
19445    }
19446    Some((*first, type_node))
19447}
19448
19449/// Whether `name` is a type parameter of a template declaration that lexically
19450/// encloses `node`. The malformed macro-return field uses the parameter name as
19451/// its pseudo-declarator; preserving that field is necessary to publish a
19452/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
19453/// ancestors instead of interpreting source text so nested templates and
19454/// parser-recovered regions retain their real lexical scopes.
19455pub(crate) fn cpp_active_template_type_parameter<'tree>(
19456    node: Node<'tree>,
19457    name: &str,
19458    source: &str,
19459    ancestry: &ParentIndex<'tree>,
19460) -> bool {
19461    let mut ancestor = ancestry.parent(node);
19462    while let Some(current) = ancestor {
19463        if current.kind() == "template_declaration"
19464            && let Some(parameters) = current.child_by_field_name("parameters")
19465            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19466                cpp_template_parameter_name(parameter, source)
19467            })
19468            .type_parameter
19469        {
19470            return true;
19471        }
19472        ancestor = ancestry.parent(current);
19473    }
19474    false
19475}
19476
19477/// Reparse the region `[start, end)` of `source` as C++, confined to the region
19478/// via included ranges so every reparsed node keeps its original byte offset and
19479/// line number. The existing visitors read node text from the original source,
19480/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
19481/// `parse_rust_region_tree` technique.
19482fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19483    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19484}
19485
19486fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19487    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19488        return None;
19489    }
19490    let semicolon = node.next_sibling()?;
19491    if semicolon.kind() != ";" || semicolon.is_missing() {
19492        return None;
19493    }
19494    let row = node.start_position().row;
19495    let mut start = node.start_byte();
19496    let mut sibling = node.prev_sibling();
19497    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19498        if previous.kind() == ";" {
19499            break;
19500        }
19501        start = previous.start_byte();
19502        sibling = previous.prev_sibling();
19503    }
19504    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19505}
19506
19507/// One `RET name _(( args ));`-style K&R prototype-macro invocation
19508/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
19509/// byte spans a clean reparse needs, in original-source order.
19510struct PrototypeMacroCandidate {
19511    /// Where the return type and declared name begin.
19512    run_start: usize,
19513    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
19514    /// exclusive end of the first reparse span.
19515    identifier_start: usize,
19516    /// The inner `(`'s start byte: the start of the second reparse span.
19517    /// This paren is kept so the reparse sees exactly one parameter-list
19518    /// paren pair.
19519    inner_open_start: usize,
19520    /// Byte just past the inner `)`: the end of the second reparse span.
19521    inner_close_end: usize,
19522    /// Byte just past the outer `)`: the start of the third reparse span.
19523    outer_close_end: usize,
19524    /// Byte just past the terminating `;`: the end of the third reparse
19525    /// span, and of the whole candidate.
19526    semicolon_end: usize,
19527}
19528
19529impl PrototypeMacroCandidate {
19530    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
19531    /// one included-range tree: the return type and name, the parenthesized
19532    /// argument list, and the terminating `;`. The macro token and its outer
19533    /// wrapping parenthesis are omitted -- deleting them is exactly what the
19534    /// (absent) preprocessor macro expansion would do.
19535    fn ranges(&self) -> [(usize, usize); 3] {
19536        [
19537            (self.run_start, self.identifier_start),
19538            (self.inner_open_start, self.inner_close_end),
19539            (self.outer_close_end, self.semicolon_end),
19540        ]
19541    }
19542}
19543
19544/// The class a class-body prototype-macro reparse keeps around the member
19545/// (#6902): the class head through its `{`, the access label the member is
19546/// written under, and the class's `}`, at their original offsets. In that
19547/// parse the recovered prototype is a real member of a real class body, so its
19548/// `static` and its place on the body's access ladder state its modifiers
19549/// exactly as an ordinary member's do.
19550struct PrototypeMacroClassEnvelope<'tree> {
19551    /// The class's enclosing node, which is the recovery's origin.
19552    class_parent: Node<'tree>,
19553    /// The class head through the body's `{`.
19554    head: (usize, usize),
19555    /// The body's `{`, which finds the reparsed body.
19556    open: (usize, usize),
19557    /// The last `access_specifier` the body writes before the member, through
19558    /// its `:`.
19559    label: Option<(usize, usize)>,
19560    /// The body's `}`.
19561    close: (usize, usize),
19562}
19563
19564/// The [`PrototypeMacroClassEnvelope`] of one class-body member, read forward
19565/// with one cursor as [`cpp_member_declared_visibility`] reads the access
19566/// ladder. A member nested in anything but preprocessor conditionals, or under
19567/// a label a macro invocation swallowed, has no envelope that reproduces its
19568/// position, so it stays unrecovered rather than taking another access.
19569fn cpp_prototype_macro_class_envelope<'tree>(
19570    member: Node<'tree>,
19571    source: &str,
19572    ancestry: &ParentIndex<'tree>,
19573) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19574    let mut list_level = member;
19575    let body = loop {
19576        let parent = ancestry.parent(list_level)?;
19577        if parent.kind() == "field_declaration_list" {
19578            break parent;
19579        }
19580        if !is_cpp_preprocessor_conditional(parent) {
19581            return None;
19582        }
19583        list_level = parent;
19584    };
19585    let class_like = ancestry.parent(body)?;
19586    let class_parent = ancestry.parent(class_like)?;
19587    let open = body.child(0).filter(|open| open.kind() == "{")?;
19588    let close = body
19589        .child(body.child_count().checked_sub(1)?)
19590        .filter(|close| close.kind() == "}" && !close.is_missing())?;
19591    let mut label = None;
19592    let mut under_swallowed_label = false;
19593    let mut cursor = body.walk();
19594    for child in body.children(&mut cursor) {
19595        if child == list_level {
19596            break;
19597        }
19598        if child.kind() == "access_specifier" {
19599            // The grammar leaves the label's `:` to the body.
19600            let colon = child
19601                .next_sibling()
19602                .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19603            label = Some((child.start_byte(), colon.end_byte()));
19604            under_swallowed_label = false;
19605        } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19606            under_swallowed_label = true;
19607        }
19608    }
19609    if under_swallowed_label {
19610        return None;
19611    }
19612    Some(PrototypeMacroClassEnvelope {
19613        class_parent,
19614        head: (class_like.start_byte(), open.end_byte()),
19615        open: (open.start_byte(), open.end_byte()),
19616        label,
19617        close: (close.start_byte(), close.end_byte()),
19618    })
19619}
19620
19621/// A live terminating semicolon owned directly by `node`.
19622fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19623    node.child(node.child_count().checked_sub(1)?)
19624        .filter(|child| child.kind() == ";" && !child.is_missing())
19625}
19626
19627/// The known pre-ANSI prototype macros whose expansion is exactly their one
19628/// argument. The malformed CST cannot prove that an arbitrary identifier has
19629/// that definition, so unknown spellings fail closed rather than turning an
19630/// ordinary broken declaration into a Function (issue #2932).
19631fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19632    matches!(
19633        node.kind(),
19634        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19635    ) && matches!(
19636        normalize_cpp_whitespace(node_text(node, source)).as_str(),
19637        "_" | "__P" | "OF" | "PROTO"
19638    )
19639}
19640
19641/// Read tree-sitter's recovery for `declared_name MACRO`: a
19642/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
19643/// declared name, and whose `name` is a known prototype macro.
19644fn cpp_prototype_macro_qualified_parts<'tree>(
19645    node: Node<'tree>,
19646    source: &str,
19647) -> Option<(Node<'tree>, Node<'tree>)> {
19648    if node.kind() != "qualified_identifier" {
19649        return None;
19650    }
19651    let declared_name = node
19652        .child_by_field_name("scope")
19653        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19654    let macro_name = macro_decorated_unqualified_name(node)?;
19655    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19656}
19657
19658/// The inner parenthesized node of tree-sitter's structured
19659/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
19660/// the macro invocation's outer parentheses while retaining the one pair an
19661/// ordinary function declarator needs. No delimiter search is involved: both
19662/// pairs and their ownership come from the CST.
19663fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19664    if arguments.kind() != "argument_list"
19665        || arguments.named_child_count() != 1
19666        || arguments.child_count() != 3
19667        || arguments
19668            .child(0)
19669            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19670        || arguments
19671            .child(2)
19672            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19673    {
19674        return None;
19675    }
19676    let inner = arguments.named_child(0)?;
19677    let close_index = match inner.kind() {
19678        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19679        // tree-sitter completes the C++ cast production with a zero-width
19680        // value after the live close parenthesis.
19681        "cast_expression" => inner.child_count().checked_sub(2)?,
19682        _ => return None,
19683    };
19684    (inner
19685        .child(0)
19686        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19687        && inner
19688            .child(close_index)
19689            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19690    .then_some(inner)
19691}
19692
19693fn cpp_prototype_macro_candidate_from_init_declaration(
19694    declaration: Node<'_>,
19695    source: &str,
19696) -> Option<PrototypeMacroCandidate> {
19697    let init = declaration
19698        .child_by_field_name("declarator")
19699        .filter(|declarator| declarator.kind() == "init_declarator")?;
19700    let malformed_declarator = init.child_by_field_name("declarator")?;
19701    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19702        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19703    } else {
19704        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19705            return None;
19706        }
19707        let declared_name_error = init
19708            .prev_named_sibling()
19709            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19710        let declared_name = declared_name_error
19711            .named_child(0)
19712            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19713        (declared_name, malformed_declarator)
19714    };
19715    let arguments = init
19716        .child_by_field_name("value")
19717        .filter(|value| value.kind() == "argument_list")?;
19718    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19719    let semicolon = cpp_direct_semicolon(declaration)?;
19720    let return_type = declaration.child_by_field_name("type")?;
19721    if return_type.end_byte() > declared_name.start_byte()
19722        || declared_name.end_byte() > macro_name.start_byte()
19723        || macro_name.end_byte() > arguments.start_byte()
19724        || arguments.end_byte() > semicolon.start_byte()
19725    {
19726        return None;
19727    }
19728    Some(cpp_prototype_macro_candidate_from_parts(
19729        declaration.start_byte(),
19730        macro_name,
19731        inner,
19732        arguments,
19733        semicolon,
19734    ))
19735}
19736
19737fn cpp_prototype_macro_candidate_from_qualified_declaration(
19738    declaration: Node<'_>,
19739    source: &str,
19740) -> Option<PrototypeMacroCandidate> {
19741    let qualified = declaration
19742        .child_by_field_name("declarator")
19743        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19744    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19745    let open_error = qualified
19746        .next_named_sibling()
19747        .filter(|next| next.kind() == "ERROR")?;
19748    let close_error = last_named_child(declaration)
19749        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19750    if open_error.child_count() < 3
19751        || open_error
19752            .child(0)
19753            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19754        || open_error
19755            .child(1)
19756            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19757        || close_error.child_count() != 2
19758        || close_error
19759            .child(0)
19760            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19761        || close_error
19762            .child(1)
19763            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19764    {
19765        return None;
19766    }
19767    let inner_open = open_error.child(1)?;
19768    let inner_close = close_error.child(0)?;
19769    let outer_close = close_error.child(1)?;
19770    let semicolon = cpp_direct_semicolon(declaration)?;
19771    let return_type = declaration.child_by_field_name("type")?;
19772    if return_type.end_byte() > qualified.start_byte()
19773        || macro_name.end_byte() > open_error.start_byte()
19774        || inner_open.start_byte() > inner_close.end_byte()
19775        || inner_close.end_byte() > outer_close.start_byte()
19776        || outer_close.end_byte() > semicolon.start_byte()
19777    {
19778        return None;
19779    }
19780    Some(PrototypeMacroCandidate {
19781        run_start: declaration.start_byte(),
19782        identifier_start: macro_name.start_byte(),
19783        inner_open_start: inner_open.start_byte(),
19784        inner_close_end: inner_close.end_byte(),
19785        outer_close_end: outer_close.end_byte(),
19786        semicolon_end: semicolon.end_byte(),
19787    })
19788}
19789
19790fn cpp_prototype_macro_candidate_from_pointer_expression(
19791    statement: Node<'_>,
19792    source: &str,
19793) -> Option<PrototypeMacroCandidate> {
19794    if statement.kind() != "expression_statement"
19795        || statement.named_child_count() != 1
19796        || !statement.has_error()
19797    {
19798        return None;
19799    }
19800    let expansion = statement
19801        .named_child(0)
19802        .filter(|child| child.kind() == "parameter_pack_expansion")?;
19803    let binary = expansion
19804        .child_by_field_name("pattern")
19805        .filter(|pattern| pattern.kind() == "binary_expression")?;
19806    if binary.child_count() != 3
19807        || binary
19808            .child(1)
19809            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19810        || expansion
19811            .child(expansion.child_count().checked_sub(1)?)
19812            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19813    {
19814        return None;
19815    }
19816    let return_type = binary.child_by_field_name("left")?;
19817    let call = binary
19818        .child_by_field_name("right")
19819        .filter(|right| right.kind() == "call_expression")?;
19820    let qualified = call.child_by_field_name("function")?;
19821    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19822    let arguments = call
19823        .child_by_field_name("arguments")
19824        .filter(|arguments| arguments.kind() == "argument_list")?;
19825    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19826    let semicolon = cpp_direct_semicolon(statement)?;
19827    if return_type.end_byte() > declared_name.start_byte()
19828        || macro_name.end_byte() > arguments.start_byte()
19829        || arguments.end_byte() > semicolon.start_byte()
19830    {
19831        return None;
19832    }
19833    Some(cpp_prototype_macro_candidate_from_parts(
19834        statement.start_byte(),
19835        macro_name,
19836        inner,
19837        arguments,
19838        semicolon,
19839    ))
19840}
19841
19842/// The inner parenthesized declarator of the `((parameters))` wrapper the
19843/// macro-declaration grammar makes of a prototype macro's argument list, with
19844/// the same span-keeping contract as [`cpp_prototype_macro_inner_arguments`]:
19845/// both paren pairs and their ownership come from the CST, and the returned
19846/// node's exact range is the one pair an ordinary function declarator needs.
19847fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
19848    if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
19849        return None;
19850    }
19851    let open = outer.child(0)?;
19852    let close = outer.child(outer.child_count().checked_sub(1)?)?;
19853    if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
19854        return None;
19855    }
19856    let inner = outer.named_child(0)?;
19857    if inner.kind() != "parenthesized_declarator" {
19858        return None;
19859    }
19860    let inner_open = inner.child(0)?;
19861    let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
19862    (inner_open.kind() == "("
19863        && !inner_open.is_missing()
19864        && inner_close.kind() == ")"
19865        && !inner_close.is_missing())
19866    .then_some(inner)
19867}
19868
19869/// Whether the macro-declaration grammar admitted a word of this declaration
19870/// as a `macro_modifier`, the structured proof that the declaration did not
19871/// parse without it.
19872fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
19873    let mut cursor = declaration.walk();
19874    declaration
19875        .named_children(&mut cursor)
19876        .any(|child| child.kind() == "macro_modifier")
19877}
19878
19879/// `RET name _((args));` as the macro-declaration grammar reads it when the
19880/// argument list stays an expression: the return-type word is admitted as a
19881/// `macro_modifier`, the declared name takes the `type` field, and the macro
19882/// token is the init declarator's callee. Same #2932 contract as the
19883/// ERROR-based readers above, on nodes that carry no `ERROR` of their own.
19884fn cpp_prototype_macro_candidate_from_macro_modifier_init(
19885    declaration: Node<'_>,
19886    source: &str,
19887) -> Option<PrototypeMacroCandidate> {
19888    if !cpp_declaration_has_macro_modifier(declaration) {
19889        return None;
19890    }
19891    let declared_name = declaration
19892        .child_by_field_name("type")
19893        .filter(|type_node| type_node.kind() == "type_identifier")?;
19894    let init = declaration
19895        .child_by_field_name("declarator")
19896        .filter(|declarator| declarator.kind() == "init_declarator")?;
19897    let macro_name = init
19898        .child_by_field_name("declarator")
19899        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
19900    let arguments = init
19901        .child_by_field_name("value")
19902        .filter(|value| value.kind() == "argument_list")?;
19903    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19904    let semicolon = cpp_direct_semicolon(declaration)?;
19905    if declared_name.end_byte() > macro_name.start_byte()
19906        || macro_name.end_byte() > arguments.start_byte()
19907        || arguments.end_byte() > semicolon.start_byte()
19908    {
19909        return None;
19910    }
19911    Some(cpp_prototype_macro_candidate_from_parts(
19912        declaration.start_byte(),
19913        macro_name,
19914        inner,
19915        arguments,
19916        semicolon,
19917    ))
19918}
19919
19920/// `RET *name _((args));` as the macro-declaration grammar reads it when the
19921/// argument list stays declarator-shaped: the declared name and the macro
19922/// token prefix the nested parenthesized declarator as the modifiers of a
19923/// `macro_modifier_declarator` chain.
19924fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
19925    declaration: Node<'_>,
19926    source: &str,
19927) -> Option<PrototypeMacroCandidate> {
19928    let mut declarator = declaration.child_by_field_name("declarator")?;
19929    while declarator.kind() == "pointer_declarator" {
19930        declarator = declarator.child_by_field_name("declarator")?;
19931    }
19932    // The macro's own modifier must be preceded by the declared name's.
19933    let mut saw_name_modifier = false;
19934    let (macro_name, arguments) = loop {
19935        if declarator.kind() != "macro_modifier_declarator" {
19936            return None;
19937        }
19938        let mut cursor = declarator.walk();
19939        let modifier = declarator
19940            .named_children(&mut cursor)
19941            .find(|child| child.kind() == "macro_modifier")
19942            .and_then(macro_modifier_identifier)?;
19943        let inner = declarator.child_by_field_name("declarator")?;
19944        if cpp_is_prototype_macro_identifier(modifier, source) {
19945            break (modifier, inner);
19946        }
19947        saw_name_modifier = true;
19948        declarator = inner;
19949    };
19950    if !saw_name_modifier {
19951        return None;
19952    }
19953    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19954    let semicolon = cpp_direct_semicolon(declaration)?;
19955    Some(cpp_prototype_macro_candidate_from_parts(
19956        declaration.start_byte(),
19957        macro_name,
19958        inner,
19959        arguments,
19960        semicolon,
19961    ))
19962}
19963
19964/// `RET name _((args));` as the macro-declaration grammar reads it when the
19965/// macro token is the one word that could stand as the type: the return-type
19966/// words and the declared name are `macro_modifier`s and the nested argument
19967/// list is the whole declarator.
19968fn cpp_prototype_macro_candidate_from_macro_modifier_type(
19969    declaration: Node<'_>,
19970    source: &str,
19971) -> Option<PrototypeMacroCandidate> {
19972    if !cpp_declaration_has_macro_modifier(declaration) {
19973        return None;
19974    }
19975    let macro_name = declaration
19976        .child_by_field_name("type")
19977        .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
19978    let arguments = declaration.child_by_field_name("declarator")?;
19979    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19980    let semicolon = cpp_direct_semicolon(declaration)?;
19981    Some(cpp_prototype_macro_candidate_from_parts(
19982        declaration.start_byte(),
19983        macro_name,
19984        inner,
19985        arguments,
19986        semicolon,
19987    ))
19988}
19989
19990/// `RET name _((args));` as the macro-declaration grammar reads it when the
19991/// argument list itself needs recovery: the return-type word is a
19992/// `macro_modifier`, the declared name takes the `type` field, the macro token
19993/// is the first declarator, and the argument list's paren pairs land in the
19994/// bracketing `ERROR`s.
19995fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
19996    declaration: Node<'_>,
19997    source: &str,
19998) -> Option<PrototypeMacroCandidate> {
19999    if !cpp_declaration_has_macro_modifier(declaration) {
20000        return None;
20001    }
20002    let declared_name = declaration
20003        .child_by_field_name("type")
20004        .filter(|type_node| type_node.kind() == "type_identifier")?;
20005    let mut declarator_cursor = declaration.walk();
20006    let macro_name = declaration
20007        .children_by_field_name("declarator", &mut declarator_cursor)
20008        .next()
20009        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20010    let open_error = macro_name
20011        .next_named_sibling()
20012        .filter(|next| next.kind() == "ERROR")?;
20013    let close_error = last_named_child(declaration)
20014        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20015    if open_error.child_count() < 3
20016        || open_error
20017            .child(0)
20018            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20019        || open_error
20020            .child(1)
20021            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20022        || close_error.child_count() != 2
20023        || close_error
20024            .child(0)
20025            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20026        || close_error
20027            .child(1)
20028            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20029    {
20030        return None;
20031    }
20032    let inner_open = open_error.child(1)?;
20033    let inner_close = close_error.child(0)?;
20034    let outer_close = close_error.child(1)?;
20035    let semicolon = cpp_direct_semicolon(declaration)?;
20036    if declared_name.end_byte() > macro_name.start_byte()
20037        || macro_name.end_byte() > open_error.start_byte()
20038        || inner_open.start_byte() > inner_close.end_byte()
20039        || inner_close.end_byte() > outer_close.start_byte()
20040        || outer_close.end_byte() > semicolon.start_byte()
20041    {
20042        return None;
20043    }
20044    Some(PrototypeMacroCandidate {
20045        run_start: declaration.start_byte(),
20046        identifier_start: macro_name.start_byte(),
20047        inner_open_start: inner_open.start_byte(),
20048        inner_close_end: inner_close.end_byte(),
20049        outer_close_end: outer_close.end_byte(),
20050        semicolon_end: semicolon.end_byte(),
20051    })
20052}
20053
20054fn cpp_prototype_macro_candidate_from_parts(
20055    run_start: usize,
20056    macro_name: Node<'_>,
20057    inner_arguments: Node<'_>,
20058    outer_arguments: Node<'_>,
20059    semicolon: Node<'_>,
20060) -> PrototypeMacroCandidate {
20061    PrototypeMacroCandidate {
20062        run_start,
20063        identifier_start: macro_name.start_byte(),
20064        inner_open_start: inner_arguments.start_byte(),
20065        inner_close_end: inner_arguments.end_byte(),
20066        outer_close_end: outer_arguments.end_byte(),
20067        semicolon_end: semicolon.end_byte(),
20068    }
20069}
20070
20071/// Recover only CST shapes whose fields preserve the declaration name, macro
20072/// invocation, nested argument list, and terminator. Arbitrary flattened
20073/// `ERROR` wreckage stays unsupported rather than being interpreted through a
20074/// token or delimiter scan (issue #2932).
20075fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20076    let candidate = match node.kind() {
20077        "declaration" | "field_declaration" => {
20078            cpp_prototype_macro_candidate_from_init_declaration(node, source)
20079                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20080                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20081                .or_else(|| {
20082                    cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20083                })
20084                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20085                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20086        }
20087        "expression_statement" => {
20088            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20089        }
20090        _ => None,
20091    };
20092    candidate.into_iter().collect()
20093}
20094
20095fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20096    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20097        return false;
20098    }
20099    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20100        return false;
20101    }
20102    let Some(declarator) = (if node.kind() == "function_definition" {
20103        node.child_by_field_name("declarator")
20104            .and_then(extract_function_declarator)
20105    } else {
20106        node.named_child(0)
20107            .filter(|child| child.kind() == "function_declarator")
20108    }) else {
20109        return false;
20110    };
20111    let Some(name) = cpp_function_declarator_name_node(declarator) else {
20112        return false;
20113    };
20114    declarator.start_byte() == node.start_byte()
20115        && name.kind() == "identifier"
20116        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20117}
20118
20119/// Reparse a fragmented class-body interior while preserving its original byte
20120/// and line offsets, confined to the region by tree-sitter included ranges.
20121///
20122/// This used to materialize the region's whole file prefix as whitespace and
20123/// make the lexer walk it, the technique #1309 replaced on the other reparse
20124/// path: O(file) per fragmented-class recovery, on files that are already
20125/// error-recovered and already slow (#2788). Included ranges give the parser
20126/// the same view -- the region's bytes, at their original offsets and
20127/// line/column positions -- without materializing or lexing anything before it.
20128///
20129/// The equality of the two views is the claim, so a debug build parses both and
20130/// asserts the trees agree node for node.
20131fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20132    let region = cpp_reparse_region_items(source, start, end);
20133
20134    #[cfg(debug_assertions)]
20135    assert_eq!(
20136        region.as_ref().map(cpp_tree_shape),
20137        cpp_reparse_padded_class_body(source, start, end)
20138            .as_ref()
20139            .map(cpp_tree_shape),
20140        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20141         prefix produces"
20142    );
20143
20144    region
20145}
20146
20147/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
20148/// replaces, kept as the oracle a debug build asserts every region reparse
20149/// against and as the release-mode parity tests' reference (#2788).
20150#[cfg(any(debug_assertions, test))]
20151fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20152    if start >= end {
20153        // The included-range parser refuses an empty region; the padded one
20154        // returned a tree holding nothing, which every caller read as "no
20155        // members here".
20156        return None;
20157    }
20158    let bytes = source.as_bytes();
20159    let prefix = bytes.get(..start)?;
20160    let interior = bytes.get(start..end)?;
20161    let mut padded = Vec::with_capacity(end);
20162    padded.extend(
20163        prefix
20164            .iter()
20165            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20166    );
20167    padded.extend_from_slice(interior);
20168    let padded = String::from_utf8(padded).ok()?;
20169    let mut parser = tree_sitter::Parser::new();
20170    parser
20171        .set_language(&tree_sitter_cpp::LANGUAGE.into())
20172        .ok()?;
20173    parser.parse(&padded, None)
20174}
20175
20176/// Every node of `tree` in preorder, by kind, span and position, which is what
20177/// two reparses of one region have to agree on for their callers to read the
20178/// same declarations out of either (#2788).
20179#[cfg(any(debug_assertions, test))]
20180fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20181    let mut shape = Vec::new();
20182    let mut cursor = tree.root_node().walk();
20183    let mut stack = vec![tree.root_node()];
20184    while let Some(node) = stack.pop() {
20185        shape.push((
20186            node.kind(),
20187            node.start_byte(),
20188            node.end_byte(),
20189            node.start_position().row,
20190            node.start_position().column,
20191            node.is_named(),
20192            node.is_missing(),
20193        ));
20194        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20195        stack.extend(children.into_iter().rev());
20196    }
20197    shape
20198}
20199
20200/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
20201/// reparsed interior is indexed only when every top-level named node is a
20202/// well-formed C++ item (or a comment) and at least one real item is present.
20203/// Expression/statement soup surfaces as a top-level `ERROR` or
20204/// `expression_statement`, neither of which is an item kind, so it is rejected.
20205///
20206/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
20207/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
20208/// swallowed by a preceding dangling sentinel) reparses to a real
20209/// `namespace_definition` whose body still holds a bogus `function_definition`,
20210/// so the subtree legitimately carries an error. Container items are admitted
20211/// even with an internal error; the inner bogus function is recovered recursively
20212/// when `visit_function_definition` walks it. Each recursion strips at least one
20213/// leading sentinel, so the region strictly shrinks and recovery terminates.
20214///
20215/// A top-level `function_definition` is the one place we stay strict: it is
20216/// admitted only when it is clean or is itself a sentinel candidate. A function
20217/// that has an error and is not a sentinel is a real callable with a broken body,
20218/// so we refuse the whole reparse and let the ordinary path handle it (preserving
20219/// its real return type rather than re-deriving an implicit one).
20220fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20221    let mut cursor = root.walk();
20222    let mut saw_item = false;
20223    for child in root.named_children(&mut cursor) {
20224        match child.kind() {
20225            "comment" => {}
20226            "function_definition" => {
20227                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20228                    return false;
20229                }
20230                saw_item = true;
20231            }
20232            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20233            _ => return false,
20234        }
20235    }
20236    saw_item
20237}
20238
20239/// Robustness gate for a reparsed fragmented multiple-base export class body
20240/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
20241/// kinds a class body produces when reparsed at translation-unit scope: the
20242/// access-specifier label preceding the first member surfaces as a
20243/// `labeled_statement` wrapping that member, and members surface as
20244/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
20245/// Statement or expression soup surfaces as other top-level kinds and is rejected,
20246/// so only a genuinely member-shaped body is ever re-owned as members; anything
20247/// ambiguous falls back to indexing the class alone.
20248fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20249    if node.kind() != "ERROR" {
20250        return false;
20251    }
20252    let mut stack = Vec::new();
20253    let mut saw_function_declarator = false;
20254    let mut cursor = node.walk();
20255    for child in node.named_children(&mut cursor) {
20256        stack.push(child);
20257    }
20258    while let Some(current) = stack.pop() {
20259        match current.kind() {
20260            // Tree-sitter may wrap adjacent copy-control declarations in a
20261            // nested ERROR. Keep descending only through ERROR wrappers; the
20262            // actual declaration payload must be a function_declarator.
20263            "ERROR" => {
20264                let mut cursor = current.walk();
20265                stack.extend(current.named_children(&mut cursor));
20266            }
20267            "function_declarator" => saw_function_declarator = true,
20268            _ => return false,
20269        }
20270    }
20271    saw_function_declarator
20272}
20273
20274fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20275    if node.kind() != "ERROR" {
20276        return false;
20277    }
20278    let mut cursor = node.walk();
20279    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20280    let [explicit, constructor_error, tail] = named.as_slice() else {
20281        return false;
20282    };
20283    if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20284        return false;
20285    }
20286    // The copy-control declarators the inner ERROR holds, with only their `;`
20287    // tokens beside them. The older grammar kept the constructor alone there
20288    // and gave the destructor the slot after it; the macro-declaration grammar
20289    // absorbs the destructor beside the constructor and folds the member
20290    // written after them into the tail slot as a `macro_modifier_declarator`.
20291    let mut declarators = Vec::new();
20292    let mut stack = vec![*constructor_error];
20293    while let Some(current) = stack.pop() {
20294        match current.kind() {
20295            "ERROR" => {
20296                let mut cursor = current.walk();
20297                let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20298                stack.extend(children.into_iter().rev());
20299            }
20300            "function_declarator" => declarators.push(current),
20301            _ => return false,
20302        }
20303    }
20304    let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20305        ([constructor], "function_declarator") => (*constructor, *tail),
20306        ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20307        _ => return false,
20308    };
20309    let Some(constructor_name) =
20310        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20311    else {
20312        return false;
20313    };
20314    let Some(destructor_name) =
20315        extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20316    else {
20317        return false;
20318    };
20319    let Some(destroyed_type) = destructor_name.named_child(0) else {
20320        return false;
20321    };
20322    constructor_name.kind() == "identifier"
20323        && destructor_name.kind() == "destructor_name"
20324        && destroyed_type.kind() == "identifier"
20325        && node_text(constructor_name, source) == node_text(destroyed_type, source)
20326}
20327
20328fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20329    if node.kind() != "compound_statement" {
20330        return false;
20331    }
20332    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20333        return false;
20334    };
20335    if prefix.kind() == "labeled_statement"
20336        && prefix.named_child(0).is_some_and(|label| {
20337            matches!(
20338                node_text(label, source).trim(),
20339                "public" | "private" | "protected"
20340            )
20341        })
20342    {
20343        return prefix.named_children(&mut prefix.walk()).any(|child| {
20344            child.kind() == "declaration"
20345                && child.has_error()
20346                && child
20347                    .named_children(&mut child.walk())
20348                    .any(cpp_reparsed_member_error_is_indexable)
20349        });
20350    }
20351    // A malformed constructor initializer can be split into a declaration
20352    // followed by its compound body when the class prefix already contains
20353    // realistic members. Keep this admission tied to that exact structured
20354    // declaration/error/body chain rather than accepting arbitrary blocks.
20355    prefix.kind() == "declaration"
20356        && prefix.has_error()
20357        && prefix
20358            .named_children(&mut prefix.walk())
20359            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20360}
20361
20362fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20363    if !cpp_reparsed_member_error_is_indexable(node) {
20364        return false;
20365    }
20366    let Some(preproc) = node.next_named_sibling() else {
20367        return false;
20368    };
20369    preproc.kind() == "preproc_if"
20370        && preproc.has_error()
20371        && preproc
20372            .named_children(&mut preproc.walk())
20373            .any(|child| child.kind() == "expression_statement" && child.has_error())
20374        && preproc
20375            .next_named_sibling()
20376            .is_some_and(|body| body.kind() == "compound_statement")
20377}
20378
20379/// Return a function body whose braces and ownership are explicit in the
20380/// reparsed class-member tree. An error below a real function envelope is
20381/// recoverable by the ordinary function visitor; a missing/deferred body is
20382/// not, because accepting it would let statement soup masquerade as a member.
20383fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20384    if node.kind() != "function_definition" {
20385        return None;
20386    }
20387    let body = node.child_by_field_name("body")?;
20388    if body.kind() != "compound_statement" {
20389        return None;
20390    }
20391    let open = body.child(0)?;
20392    let close = body.child(body.child_count().checked_sub(1)?)?;
20393    if open.kind() != "{"
20394        || open.is_missing()
20395        || close.kind() != "}"
20396        || close.is_missing()
20397        || close.end_byte() != body.end_byte()
20398        || body.end_byte() != node.end_byte()
20399    {
20400        return None;
20401    }
20402    Some(body)
20403}
20404
20405fn cpp_reparsed_member_function_errors_are_in_body(
20406    node: Node<'_>,
20407    body: Node<'_>,
20408    source: &str,
20409) -> bool {
20410    let mut cursor = node.walk();
20411    node.children(&mut cursor).all(|child| {
20412        same_node(child, body)
20413            || cpp_reparsed_member_attribute_error(child, source)
20414            || cpp_reparsed_member_signature_identifier_errors(child)
20415            || (!child.has_error() && !child.is_error() && !child.is_missing())
20416    })
20417}
20418
20419/// A complete callable can still carry parser errors in its signature when a
20420/// project annotation is not part of the C++ grammar (`nonneg int`,
20421/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
20422/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
20423/// leaves inside the already-proven callable envelope; structured statements,
20424/// literals, missing tokens, and other malformed signature payload remain
20425/// rejected.
20426fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20427    if !node.has_error() && !node.is_error() && !node.is_missing() {
20428        return false;
20429    }
20430    let mut stack = vec![node];
20431    let mut saw_error = false;
20432    while let Some(current) = stack.pop() {
20433        if current.is_missing() {
20434            return false;
20435        }
20436        if current.kind() == "ERROR" {
20437            saw_error = true;
20438            let mut cursor = current.walk();
20439            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20440            if children
20441                .iter()
20442                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20443            {
20444                return false;
20445            }
20446            stack.extend(children);
20447            continue;
20448        }
20449        let mut cursor = current.walk();
20450        stack.extend(current.children(&mut cursor));
20451    }
20452    saw_error
20453}
20454
20455fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20456    node.kind() == "ERROR"
20457        && node.named_child_count() == 1
20458        && node
20459            .named_child(0)
20460            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20461}
20462
20463fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20464    node.kind() == "identifier"
20465        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20466}
20467
20468/// A C++ attribute placed between a member's declarator and body can make
20469/// tree-sitter expose the callable as
20470/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
20471/// Keep this admission tied to that exact node geometry. In particular, an
20472/// arbitrary ERROR or identifier before a compound statement is not enough.
20473fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20474    let Some(body) = cpp_reparsed_member_function_body(node) else {
20475        return false;
20476    };
20477    let mut cursor = node.walk();
20478    let named = node
20479        .named_children(&mut cursor)
20480        .filter(|child| child.kind() != "comment")
20481        .collect::<Vec<_>>();
20482    let [type_node, error, attribute, body_node] = named.as_slice() else {
20483        return false;
20484    };
20485    if !same_node(*body_node, body)
20486        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20487        || !cpp_reparsed_export_macro_identifier(*attribute, source)
20488        || error.kind() != "ERROR"
20489        || error.named_child_count() != 1
20490    {
20491        return false;
20492    }
20493    error
20494        .named_child(0)
20495        .is_some_and(cpp_reparsed_attribute_callable_declarator)
20496}
20497
20498fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20499    cpp_structured_type_path(node, source).is_some()
20500        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20501}
20502
20503fn cpp_reparsed_member_return_and_declarator_are_indexable(
20504    return_error: Node<'_>,
20505    declarator: Node<'_>,
20506    body_node: Node<'_>,
20507    body: Node<'_>,
20508    source: &str,
20509) -> bool {
20510    return_error.kind() == "ERROR"
20511        && return_error.named_child_count() == 1
20512        && same_node(body_node, body)
20513        && return_error.named_child(0).is_some_and(|return_type| {
20514            cpp_reparsed_member_return_type_is_indexable(return_type, source)
20515        })
20516        && extract_function_declarator(declarator)
20517            .and_then(cpp_function_declarator_name_node)
20518            .is_some()
20519}
20520
20521fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20522    let Some(body) = cpp_reparsed_member_function_body(node) else {
20523        return false;
20524    };
20525    let mut cursor = node.walk();
20526    let named = node
20527        .named_children(&mut cursor)
20528        .filter(|child| child.kind() != "comment")
20529        .collect::<Vec<_>>();
20530    let [friend, return_error, declarator, body_node] = named.as_slice() else {
20531        return false;
20532    };
20533    friend.kind() == "type_identifier"
20534        && node_text(*friend, source) == "friend"
20535        && cpp_reparsed_member_return_and_declarator_are_indexable(
20536            *return_error,
20537            *declarator,
20538            *body_node,
20539            body,
20540            source,
20541        )
20542}
20543
20544fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20545    let Some(body) = cpp_reparsed_member_function_body(node) else {
20546        return false;
20547    };
20548    let mut cursor = node.walk();
20549    let named = node
20550        .named_children(&mut cursor)
20551        .filter(|child| child.kind() != "comment")
20552        .collect::<Vec<_>>();
20553    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20554        return false;
20555    };
20556    prefix
20557        .iter()
20558        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20559        && attribute.kind() == "type_identifier"
20560        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20561        && cpp_reparsed_member_return_and_declarator_are_indexable(
20562            *return_error,
20563            *declarator,
20564            *body_node,
20565            body,
20566            source,
20567        )
20568}
20569
20570/// An included-range reparse that begins inside a malformed class can merge an
20571/// access label and following template member. Tree-sitter then emits the label
20572/// as the `template_type` name, the template parameter list as its arguments,
20573/// an ERROR-wrapped return type, the callable declarator, and its complete
20574/// body. Admit only that exact structured displacement.
20575fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20576    let Some(body) = cpp_reparsed_member_function_body(node) else {
20577        return false;
20578    };
20579    let mut cursor = node.walk();
20580    let named = node
20581        .named_children(&mut cursor)
20582        .filter(|child| child.kind() != "comment")
20583        .collect::<Vec<_>>();
20584    // The member's return type is an `ERROR` of its own, or -- under the
20585    // macro-declaration grammar, which admits it as a modifier of the
20586    // declarator it could not separate -- folded into the declarator's
20587    // `macro_modifier_declarator` wrapper.
20588    let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20589        [template_type, return_error, declarator, body_node] => {
20590            let Some(return_type) = return_error.named_child(0) else {
20591                return false;
20592            };
20593            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20594                return false;
20595            }
20596            (*template_type, return_type, *declarator, *body_node)
20597        }
20598        [template_type, declarator, body_node] => {
20599            let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20600                .then(|| {
20601                    let mut cursor = declarator.walk();
20602                    declarator
20603                        .named_children(&mut cursor)
20604                        .find(|child| child.kind() == "macro_modifier")
20605                })
20606                .flatten()
20607                .and_then(macro_modifier_identifier)
20608            else {
20609                return false;
20610            };
20611            let Some(function) = declarator.child_by_field_name("declarator") else {
20612                return false;
20613            };
20614            (*template_type, return_type, function, *body_node)
20615        }
20616        _ => return false,
20617    };
20618    let Some(template_name) = template_type.child_by_field_name("name") else {
20619        return false;
20620    };
20621    let Some(arguments) = template_type.child_by_field_name("arguments") else {
20622        return false;
20623    };
20624    let mut cursor = template_type.walk();
20625    let template_errors = template_type
20626        .named_children(&mut cursor)
20627        .filter(|child| child.kind() == "ERROR")
20628        .collect::<Vec<_>>();
20629    let [comment_error] = template_errors.as_slice() else {
20630        return false;
20631    };
20632    let mut cursor = comment_error.walk();
20633    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20634    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20635        return false;
20636    };
20637    same_node(body_node, body)
20638        && template_type.kind() == "template_type"
20639        && template_name.kind() == "type_identifier"
20640        && matches!(
20641            node_text(template_name, source).trim(),
20642            "public" | "private" | "protected"
20643        )
20644        && arguments.kind() == "template_argument_list"
20645        && arguments.named_child_count() > 0
20646        && !arguments.has_error()
20647        && !colon.is_named()
20648        && colon.kind() == ":"
20649        && comments.iter().all(|child| child.kind() == "comment")
20650        && !template_keyword.is_named()
20651        && template_keyword.kind() == "template"
20652        && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20653        && extract_function_declarator(declarator)
20654            .and_then(cpp_function_declarator_name_node)
20655            .is_some()
20656}
20657
20658/// Return the constructor declaration tree-sitter can merge into an access
20659/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
20660/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
20661/// declaration's apparent type; the callable name must still exactly match the
20662/// recovered class, so unrelated labeled statements are never re-owned.
20663fn cpp_reparsed_preprocessor_constructor<'tree>(
20664    node: Node<'tree>,
20665    class_name: &str,
20666    source: &str,
20667) -> Option<Node<'tree>> {
20668    if node.kind() != "labeled_statement" {
20669        return None;
20670    }
20671    let mut cursor = node.walk();
20672    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20673    let [label, directive_error, declaration] = named.as_slice() else {
20674        return None;
20675    };
20676    if label.kind() != "statement_identifier"
20677        || !matches!(
20678            node_text(*label, source),
20679            "public" | "private" | "protected"
20680        )
20681        || directive_error.kind() != "ERROR"
20682        || directive_error.child_count() != 1
20683        || directive_error
20684            .child(0)
20685            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20686        || declaration.kind() != "declaration"
20687        || declaration.named_child_count() != 2
20688    {
20689        return None;
20690    }
20691    let apparent_type = declaration.child_by_field_name("type")?;
20692    if apparent_type.kind() != "type_identifier"
20693        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20694    {
20695        return None;
20696    }
20697    let declarator = declaration.child_by_field_name("declarator")?;
20698    let function = extract_function_declarator(declarator)?;
20699    let name = cpp_function_declarator_name_node(function)?;
20700    (node_text(name, source) == class_name).then_some(*declaration)
20701}
20702
20703fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20704    if extract_function_declarator(node)
20705        .and_then(cpp_function_declarator_name_node)
20706        .is_some()
20707    {
20708        return true;
20709    }
20710    node.kind() == "init_declarator"
20711        && node
20712            .child_by_field_name("declarator")
20713            .is_some_and(|declarator| declarator.kind() == "identifier")
20714        && node
20715            .child_by_field_name("value")
20716            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20717}
20718
20719/// Return true for the constrained/attribute form that tree-sitter splits into
20720/// an ERROR declaration, a preprocessor `requires` clause, and a following
20721/// compound statement. The three nodes must remain immediate named siblings;
20722/// this deliberately does not search source text or skip unrelated statements.
20723fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20724    if node.kind() != "ERROR" || node.named_child_count() != 3 {
20725        return false;
20726    }
20727    let mut cursor = node.walk();
20728    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20729    let [type_node, function_declarator, attribute] = named.as_slice() else {
20730        return false;
20731    };
20732    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20733        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20734        || attribute.kind() != "identifier"
20735        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20736    {
20737        return false;
20738    }
20739    let Some(preproc) =
20740        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20741    else {
20742        return false;
20743    };
20744    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20745        .filter(|sibling| sibling.kind() == "compound_statement")
20746    else {
20747        return false;
20748    };
20749    let Some(open) = body.child(0) else {
20750        return false;
20751    };
20752    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20753        return false;
20754    };
20755    let Some(condition) = preproc.child_by_field_name("condition") else {
20756        return false;
20757    };
20758    let mut cursor = preproc.walk();
20759    let payload = preproc
20760        .named_children(&mut cursor)
20761        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20762        .collect::<Vec<_>>();
20763    let [requires_statement] = payload.as_slice() else {
20764        return false;
20765    };
20766    let requires_clause = requires_statement.named_child(0);
20767
20768    open.kind() == "{"
20769        && !open.is_missing()
20770        && close.kind() == "}"
20771        && !close.is_missing()
20772        && close.end_byte() == body.end_byte()
20773        && requires_statement.kind() == "expression_statement"
20774        && requires_statement.named_child_count() == 1
20775        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20776}
20777
20778fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20779    let mut sibling = node.next_named_sibling();
20780    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20781        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20782    }
20783    sibling
20784}
20785
20786fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20787    let mut sibling = node.prev_named_sibling();
20788    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20789        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20790    }
20791    sibling
20792}
20793
20794fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20795    let Some(preproc) =
20796        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20797    else {
20798        return false;
20799    };
20800    let Some(error) =
20801        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20802    else {
20803        return false;
20804    };
20805    cpp_reparsed_attribute_requires_error(error, source)
20806}
20807
20808fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20809    parameter: Node<'tree>,
20810    macro_name: Node<'tree>,
20811    message: Node<'tree>,
20812    source: &str,
20813) -> Option<Node<'tree>> {
20814    let parameter_name = parameter.named_child(0)?;
20815    (parameter.kind() == "type_parameter_declaration"
20816        && parameter_name.kind() == "type_identifier"
20817        && matches!(macro_name.kind(), "identifier" | "type_identifier")
20818        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20819        && message.kind() == "string_literal")
20820        .then_some(parameter_name)
20821}
20822
20823fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
20824    node: Node<'tree>,
20825    source: &str,
20826) -> Option<Node<'tree>> {
20827    if node.kind() != "ERROR" {
20828        return None;
20829    }
20830    let mut cursor = node.walk();
20831    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20832    let [parameter, macro_name, message] = named.as_slice() else {
20833        return None;
20834    };
20835    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
20836}
20837
20838/// Recognize the alternate constraint-macro prefix where tree-sitter retains
20839/// the complete qualified constraint as a fourth child instead of moving it
20840/// into the following function. Keep the gate tied to a two-type template
20841/// constraint that names the declared type parameter.
20842fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
20843    node: Node<'tree>,
20844    source: &str,
20845) -> Option<Node<'tree>> {
20846    if node.kind() != "ERROR" {
20847        return None;
20848    }
20849    let mut cursor = node.walk();
20850    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20851    let [parameter, macro_name, message, constraint] = named.as_slice() else {
20852        return None;
20853    };
20854    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
20855        *parameter,
20856        *macro_name,
20857        *message,
20858        source,
20859    )?;
20860    let constraint_scope = constraint.child_by_field_name("scope")?;
20861    let constraint_template = constraint.child_by_field_name("name")?;
20862    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20863    let mut argument_cursor = constraint_arguments.walk();
20864    let constraint_types = constraint_arguments
20865        .named_children(&mut argument_cursor)
20866        .collect::<Vec<_>>();
20867    if constraint.kind() != "qualified_identifier"
20868        || constraint_scope.kind() != "namespace_identifier"
20869        || !matches!(
20870            constraint_template.kind(),
20871            "template_function" | "template_type"
20872        )
20873        || !matches!(constraint_types.as_slice(), [left, right]
20874            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20875        || constraint_arguments.has_error()
20876    {
20877        return None;
20878    }
20879    let parameter_text = node_text(parameter_name, source);
20880    let mut stack = constraint_types;
20881    while let Some(current) = stack.pop() {
20882        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
20883            return Some(parameter_name);
20884        }
20885        let mut cursor = current.walk();
20886        stack.extend(current.named_children(&mut cursor));
20887    }
20888    None
20889}
20890
20891fn cpp_reparsed_template_macro_companion_is_indexable(
20892    node: Node<'_>,
20893    parameter_name: Node<'_>,
20894    source: &str,
20895) -> bool {
20896    let Some(body) = cpp_reparsed_member_function_body(node) else {
20897        return false;
20898    };
20899    let mut cursor = node.walk();
20900    let named = node
20901        .named_children(&mut cursor)
20902        .filter(|child| child.kind() != "comment")
20903        .collect::<Vec<_>>();
20904    // The member's return type is an `ERROR` of its own, or -- under the
20905    // macro-declaration grammar, which admits it as a modifier of the
20906    // declarator it could not separate -- folded into the declarator's
20907    // `macro_modifier_declarator` wrapper.
20908    let (constraint, close_error, storage, return_type, declarator, body_node) = match named
20909        .as_slice()
20910    {
20911        [
20912            constraint,
20913            close_error,
20914            storage,
20915            return_error,
20916            declarator,
20917            body_node,
20918        ] => {
20919            let Some(return_type) = return_error.named_child(0) else {
20920                return false;
20921            };
20922            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20923                return false;
20924            }
20925            (
20926                *constraint,
20927                *close_error,
20928                *storage,
20929                return_type,
20930                *declarator,
20931                *body_node,
20932            )
20933        }
20934        [constraint, close_error, storage, declarator, body_node] => {
20935            let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
20936                return false;
20937            };
20938            (
20939                *constraint,
20940                *close_error,
20941                *storage,
20942                return_type,
20943                function,
20944                *body_node,
20945            )
20946        }
20947        _ => return false,
20948    };
20949    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
20950        return false;
20951    };
20952    let Some(constraint_template) = constraint.child_by_field_name("name") else {
20953        return false;
20954    };
20955    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
20956        return false;
20957    };
20958    let mut cursor = constraint_arguments.walk();
20959    let constraint_types = constraint_arguments
20960        .named_children(&mut cursor)
20961        .collect::<Vec<_>>();
20962    same_node(body_node, body)
20963        && constraint.kind() == "qualified_identifier"
20964        && constraint_scope.kind() == "namespace_identifier"
20965        && constraint_template.kind() == "template_type"
20966        && matches!(constraint_types.as_slice(), [left, right]
20967            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20968        && !constraint_arguments.has_error()
20969        && close_error.kind() == "ERROR"
20970        && close_error.named_child_count() == 0
20971        && storage.kind() == "storage_class_specifier"
20972        && return_type.kind() == "identifier"
20973        && node_text(return_type, source) == node_text(parameter_name, source)
20974        && extract_function_declarator(declarator)
20975            .and_then(cpp_function_declarator_name_node)
20976            .is_some()
20977}
20978
20979fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
20980    node: Node<'tree>,
20981    parameter_name: Node<'_>,
20982    source: &str,
20983) -> Option<Node<'tree>> {
20984    let body = cpp_reparsed_member_function_body(node)?;
20985    let constraint = node.child_by_field_name("type")?;
20986    let constraint_template = constraint.child_by_field_name("name")?;
20987    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20988    let mut argument_cursor = constraint_arguments.walk();
20989    let constraint_types = constraint_arguments
20990        .named_children(&mut argument_cursor)
20991        .collect::<Vec<_>>();
20992    if constraint.kind() != "qualified_identifier"
20993        || constraint_template.kind() != "template_type"
20994        || !matches!(constraint_types.as_slice(), [left, right]
20995            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20996        || constraint_arguments.has_error()
20997        || node
20998            .child_by_field_name("body")
20999            .is_none_or(|candidate| !same_node(candidate, body))
21000    {
21001        return None;
21002    }
21003
21004    let mut cursor = node.walk();
21005    let recovery_errors = node
21006        .named_children(&mut cursor)
21007        .filter(|child| child.kind() == "ERROR")
21008        .collect::<Vec<_>>();
21009    if !recovery_errors
21010        .iter()
21011        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21012        || !recovery_errors.iter().all(|error| {
21013            error.named_child_count() == 0
21014                || cpp_reparsed_constraint_macro_error(*error, source)
21015                || (error.named_child_count() == 1
21016                    && error
21017                        .named_child(0)
21018                        .is_some_and(|child| child.kind() == "function_declarator"))
21019        })
21020    {
21021        return None;
21022    }
21023
21024    let parameter_text = node_text(parameter_name, source);
21025    let mut declarators = node
21026        .child_by_field_name("declarator")
21027        .and_then(extract_function_declarator)
21028        .into_iter()
21029        .collect::<Vec<_>>();
21030    for error in recovery_errors {
21031        let mut stack = vec![error];
21032        while let Some(current) = stack.pop() {
21033            if current.kind() == "function_declarator" {
21034                declarators.push(current);
21035            }
21036            let mut cursor = current.walk();
21037            stack.extend(current.named_children(&mut cursor));
21038        }
21039    }
21040    declarators.into_iter().find(|declarator| {
21041        cpp_function_declarator_name_node(*declarator)
21042            .is_some_and(|name| name.kind() == "identifier")
21043            && declarator
21044                .child_by_field_name("parameters")
21045                .is_some_and(|parameters| {
21046                    parameters
21047                        .named_children(&mut parameters.walk())
21048                        .filter_map(|parameter| parameter.child_by_field_name("type"))
21049                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21050                })
21051    })
21052}
21053
21054fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21055    node: Node<'_>,
21056    parameter_name: Node<'_>,
21057    source: &str,
21058) -> bool {
21059    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21060}
21061
21062fn cpp_reparsed_template_macro_function_companion_is_indexable(
21063    node: Node<'_>,
21064    parameter_name: Node<'_>,
21065    source: &str,
21066) -> bool {
21067    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21068        return false;
21069    }
21070    let Some(return_type) = node.child_by_field_name("type") else {
21071        return false;
21072    };
21073    let Some(function_declarator) = node
21074        .child_by_field_name("declarator")
21075        .and_then(extract_function_declarator)
21076    else {
21077        return false;
21078    };
21079    if cpp_function_declarator_name_node(function_declarator).is_none()
21080        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21081    {
21082        return false;
21083    }
21084    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21085        return false;
21086    };
21087    let parameter_text = node_text(parameter_name, source);
21088    parameters
21089        .named_children(&mut parameters.walk())
21090        .any(|parameter| {
21091            parameter
21092                .child_by_field_name("type")
21093                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21094        })
21095}
21096
21097fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21098    if node.kind() != "ERROR" {
21099        return false;
21100    }
21101    let mut stack = vec![node];
21102    while let Some(current) = stack.pop() {
21103        let macro_shape = match current.kind() {
21104            "call_expression" => current
21105                .child_by_field_name("function")
21106                .zip(current.child_by_field_name("arguments")),
21107            "init_declarator" => current
21108                .child_by_field_name("declarator")
21109                .zip(current.child_by_field_name("value")),
21110            _ => None,
21111        };
21112        if let Some((name, arguments)) = macro_shape
21113            && name.kind() == "identifier"
21114            && arguments.kind() == "argument_list"
21115            && arguments.named_child_count() >= 2
21116            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21117        {
21118            return true;
21119        }
21120        let mut cursor = current.walk();
21121        stack.extend(current.named_children(&mut cursor));
21122    }
21123    false
21124}
21125
21126fn cpp_recovered_template_macro_constructor<'tree>(
21127    node: Node<'tree>,
21128    source: &str,
21129) -> Option<(Node<'tree>, Node<'tree>)> {
21130    let mut prefix = node.prev_named_sibling()?;
21131    while prefix.kind() == "comment" {
21132        prefix = prefix.prev_named_sibling()?;
21133    }
21134    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21135    let parameter = parameter_name
21136        .parent()
21137        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21138    let declarator =
21139        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21140    Some((declarator, parameter))
21141}
21142
21143/// The template parameter a split constraint-macro head declares for the
21144/// member function after it, when the recovery gate admits the pair.
21145/// `template <class U, REQUIRES("...", std::is_convertible<U, int>)>` leaves
21146/// the head as an `ERROR` beside a clean function definition; the parameter is
21147/// the head's structured `type_parameter_declaration`.
21148fn cpp_recovered_template_macro_function_parameter<'tree>(
21149    node: Node<'tree>,
21150    source: &str,
21151) -> Option<Node<'tree>> {
21152    let prefix = cpp_prev_non_comment_named_sibling(node)?;
21153    let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21154    if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21155        return None;
21156    }
21157    parameter_name
21158        .parent()
21159        .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21160}
21161
21162fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21163    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21164        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21165            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21166                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21167                    function,
21168                    parameter_name,
21169                    source,
21170                )
21171        });
21172    }
21173    let Some(parameter_name) =
21174        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21175    else {
21176        return false;
21177    };
21178    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21179        cpp_reparsed_template_macro_function_companion_is_indexable(
21180            function,
21181            parameter_name,
21182            source,
21183        )
21184    })
21185}
21186
21187fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21188    let function_name = node
21189        .child_by_field_name("declarator")
21190        .and_then(extract_function_declarator)
21191        .and_then(cpp_function_declarator_name_node);
21192    if let Some(body) = cpp_reparsed_member_function_body(node)
21193        && function_name.is_some()
21194        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21195    {
21196        return true;
21197    }
21198    cpp_reparsed_attribute_member_function(node, source)
21199        || cpp_reparsed_friend_function_is_indexable(node, source)
21200        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21201        || cpp_reparsed_access_template_function_is_indexable(node, source)
21202        || cpp_recovered_template_macro_constructor(node, source).is_some()
21203}
21204
21205/// Recognize the three top-level nodes produced when an unknown attribute
21206/// macro separates an inline member's declarator from its body in a reparsed
21207/// class interior: an errorful declaration with a missing semicolon, the macro
21208/// call expression, and the complete compound body. Their adjacency and exact
21209/// structured shapes prove one recoverable member envelope; arbitrary calls or
21210/// blocks do not pass this gate.
21211fn cpp_reparsed_macro_attribute_member_sequence(
21212    children: &[Node<'_>],
21213    index: usize,
21214    source: &str,
21215) -> bool {
21216    let Some(prefix) = children.get(index).copied() else {
21217        return false;
21218    };
21219    let declaration = if prefix.kind() == "labeled_statement" {
21220        prefix
21221            .named_child(prefix.named_child_count().saturating_sub(1))
21222            .filter(|child| child.kind() == "declaration")
21223    } else {
21224        (prefix.kind() == "declaration").then_some(prefix)
21225    };
21226    let Some(declaration) = declaration else {
21227        return false;
21228    };
21229    if !declaration.has_error()
21230        || declaration
21231            .child_by_field_name("declarator")
21232            .and_then(extract_function_declarator)
21233            .and_then(cpp_function_declarator_name_node)
21234            .is_none()
21235    {
21236        return false;
21237    }
21238    let Some(attribute_statement) = children.get(index + 1).copied() else {
21239        return false;
21240    };
21241    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21242        .then(|| attribute_statement.named_child(0))
21243        .flatten()
21244        .filter(|child| child.kind() == "call_expression")
21245    else {
21246        return false;
21247    };
21248    let Some(attribute_name) = attribute_call
21249        .child_by_field_name("function")
21250        .filter(|function| function.kind() == "identifier")
21251        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21252    else {
21253        return false;
21254    };
21255    if !cpp_export_macro_token(&attribute_name) {
21256        return false;
21257    }
21258    let Some(body) = children.get(index + 2).copied() else {
21259        return false;
21260    };
21261    body.kind() == "compound_statement"
21262        && body.child(0).is_some_and(|open| open.kind() == "{")
21263        && body
21264            .child(body.child_count().saturating_sub(1))
21265            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21266        && declaration.end_byte() <= attribute_statement.start_byte()
21267        && attribute_statement.end_byte() <= body.start_byte()
21268}
21269
21270/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
21271/// types and specifiers followed by a declarator, over and over, with nothing
21272/// left over. A string-argument attribute macro is what strands them there, and
21273/// the walk indexes exactly what this reads, so admitting the region is safe
21274/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
21275/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
21276/// the whole class body.
21277fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21278    if node.kind() != "ERROR" {
21279        return false;
21280    }
21281    let run = stranded_declaration_run(node, source);
21282    run.complete && !run.declarations.is_empty()
21283}
21284
21285/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
21286/// translation-unit-shaped class-body reparse as an assignment expression:
21287/// `Ctor() = default`. Admit only the exact assignment shape, and only when
21288/// the same reparse retains a separate constructor declarator for that name.
21289/// The second condition keeps an arbitrary expression statement from becoming
21290/// a member merely because it contains a callee followed by `default`.
21291/// A constructor declaration a statement-context class-body reparse reads as
21292/// a bare call statement (`value(pn_data_t* d);`). At statement scope a
21293/// constructor declaration is indistinguishable from a call, but a direct
21294/// child of the reparse root whose callee names the class can only be the
21295/// constructor: a member with no return type is nothing else.
21296fn cpp_reparsed_bare_constructor_call<'tree>(
21297    node: Node<'tree>,
21298    class_name: &str,
21299    source: &str,
21300) -> Option<Node<'tree>> {
21301    if node.kind() != "expression_statement" {
21302        return None;
21303    }
21304    let call = node
21305        .named_child(0)
21306        .filter(|child| child.kind() == "call_expression")?;
21307    let function = call.child_by_field_name("function")?;
21308    (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21309        .then_some(call)
21310}
21311
21312fn cpp_reparsed_defaulted_constructor_is_indexable(
21313    node: Node<'_>,
21314    members: &[Node<'_>],
21315    class_name: Option<&str>,
21316    source: &str,
21317) -> bool {
21318    let Some(class_name) = class_name else {
21319        return false;
21320    };
21321    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21322        return false;
21323    }
21324    let Some(assignment) = node.named_child(0) else {
21325        return false;
21326    };
21327    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21328        return false;
21329    }
21330    if assignment
21331        .child(1)
21332        .is_none_or(|operator| operator.kind() != "=")
21333    {
21334        return false;
21335    }
21336    let Some(call) = assignment.child_by_field_name("left") else {
21337        return false;
21338    };
21339    let Some(default_value) = assignment.child_by_field_name("right") else {
21340        return false;
21341    };
21342    let Some(function) = call.child_by_field_name("function") else {
21343        return false;
21344    };
21345    let Some(arguments) = call.child_by_field_name("arguments") else {
21346        return false;
21347    };
21348    if call.kind() != "call_expression"
21349        || function.kind() != "identifier"
21350        || arguments.kind() != "argument_list"
21351        || arguments.named_child_count() != 0
21352        || default_value.kind() != "identifier"
21353        || node_text(default_value, source) != "default"
21354        || node_text(function, source) != class_name
21355    {
21356        return false;
21357    }
21358    members.iter().any(|member| {
21359        if same_node(*member, node) {
21360            return false;
21361        }
21362        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21363    })
21364}
21365
21366/// Return the constructor name from one direct class-member-shaped reparse
21367/// item. The function-declarator in an `ERROR` is admitted only through the
21368/// existing ERROR-only member proof; clean function definitions must carry no
21369/// return type and must retain either a body or ABI 15's `default_method_clause`.
21370fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21371    let function_declarator = match node.kind() {
21372        "function_definition"
21373            if !node.has_error() && node.child_by_field_name("type").is_none() =>
21374        {
21375            let has_body = node
21376                .child_by_field_name("body")
21377                .is_some_and(|body| body.kind() == "compound_statement");
21378            let has_default_method_clause = node
21379                .named_children(&mut node.walk())
21380                .any(|child| child.kind() == "default_method_clause");
21381            if !has_body && !has_default_method_clause {
21382                return None;
21383            }
21384            node.child_by_field_name("declarator")
21385                .and_then(extract_function_declarator)
21386        }
21387        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21388            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21389            let mut candidate = None;
21390            while let Some(current) = stack.pop() {
21391                if current.kind() == "function_declarator" {
21392                    if candidate.is_some() {
21393                        return None;
21394                    }
21395                    candidate = Some(current);
21396                    continue;
21397                }
21398                if current.kind() == "ERROR" {
21399                    let mut cursor = current.walk();
21400                    stack.extend(current.named_children(&mut cursor));
21401                }
21402            }
21403            candidate
21404        }
21405        _ => None,
21406    }?;
21407    cpp_function_declarator_name_node(function_declarator)
21408        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21409}
21410
21411fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21412    cpp_reparsed_members_are_indexable_for_class(root, source, None)
21413}
21414
21415fn cpp_reparsed_members_are_indexable_for_class(
21416    root: Node<'_>,
21417    source: &str,
21418    class_name: Option<&str>,
21419) -> bool {
21420    let mut saw_member = false;
21421    let mut regions = Vec::new();
21422    let mut region_tree: Option<Tree> = None;
21423    loop {
21424        let root = region_tree.as_ref().map_or(root, Tree::root_node);
21425        let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21426        let mut index = 0;
21427        while index < children.len() {
21428            let child = children[index];
21429            if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21430                saw_member = true;
21431                index += 3;
21432                continue;
21433            }
21434            if let Some(recovered) = fragmented_class_body(child, source) {
21435                let Some(tree) = cpp_reparse_fragmented_class_body(
21436                    source,
21437                    recovered.body.reparse_start,
21438                    recovered.body.reparse_end,
21439                ) else {
21440                    return false;
21441                };
21442                if !cpp_reparsed_members_are_indexable_for_class(
21443                    tree.root_node(),
21444                    source,
21445                    Some(recovered.name.as_str()),
21446                ) {
21447                    return false;
21448                }
21449                saw_member = true;
21450                index += 1;
21451                while index < children.len()
21452                    && children[index].end_byte() <= recovered.body.class_range.end_byte
21453                {
21454                    index += 1;
21455                }
21456                continue;
21457            }
21458            match child.kind() {
21459                "comment" => {}
21460                "labeled_statement" => {
21461                    let Some(label) = child.child_by_field_name("label") else {
21462                        return false;
21463                    };
21464                    if !matches!(node_text(label, source), "public" | "protected" | "private") {
21465                        return false;
21466                    }
21467                    let payload = child
21468                        .named_children(&mut child.walk())
21469                        .filter(|node| node.id() != label.id() && node.kind() != "comment")
21470                        .collect::<Vec<_>>();
21471                    if payload.is_empty() {
21472                        return false;
21473                    }
21474                    // The statement-context grammar splits these constructors
21475                    // across an ERROR and initializer-looking declarations.
21476                    if class_name.is_some_and(|name| {
21477                        cpp_reparsed_synthetic_initializer_constructor(
21478                            child,
21479                            name,
21480                            source,
21481                            child.end_byte(),
21482                        )
21483                        .is_some()
21484                            || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21485                    }) {
21486                        saw_member = true;
21487                        index += 1;
21488                        continue;
21489                    }
21490                    // A destructor in statement context is a unary '~' call,
21491                    // with a possible stranded virtual keyword ahead of it.
21492                    let destructor = match payload.as_slice() {
21493                        [expression] => Some(*expression),
21494                        [specifier, expression]
21495                            if specifier.kind() == "ERROR"
21496                                && specifier.child_count() == 1
21497                                && specifier
21498                                    .child(0)
21499                                    .is_some_and(|keyword| keyword.kind() == "virtual") =>
21500                        {
21501                            Some(*expression)
21502                        }
21503                        _ => None,
21504                    };
21505                    if destructor.is_some_and(|expression| {
21506                        expression.kind() == "expression_statement"
21507                            && !expression.has_error()
21508                            && expression.named_child(0).is_some_and(|unary| {
21509                                unary.kind() == "unary_expression"
21510                                    && unary
21511                                        .child_by_field_name("operator")
21512                                        .is_some_and(|operator| operator.kind() == "~")
21513                                    && unary.child_by_field_name("argument").is_some_and(|call| {
21514                                        call.kind() == "call_expression"
21515                                            && call.child_by_field_name("function").is_some_and(
21516                                                |name| {
21517                                                    name.kind() == "identifier"
21518                                                        && class_name.is_none_or(|owner| {
21519                                                            node_text(name, source) == owner
21520                                                        })
21521                                                },
21522                                            )
21523                                            && call.child_by_field_name("arguments").is_some_and(
21524                                                |arguments| arguments.named_child_count() == 0,
21525                                            )
21526                                    })
21527                            })
21528                    }) {
21529                        saw_member = true;
21530                    } else {
21531                        // A label puts its payload in statement context, where
21532                        // static assertions look like calls and alias templates
21533                        // can become ERROR nodes. Reparse after the colon as
21534                        // declarations and apply this same gate to that region.
21535                        let colon = child
21536                            .children(&mut child.walk())
21537                            .find(|node| node.kind() == ":")
21538                            .expect("an access label has a colon");
21539                        regions.push(colon.end_byte()..child.end_byte());
21540                    }
21541                }
21542                "function_definition" => {
21543                    if child.has_error()
21544                        && !cpp_reparsed_member_function_is_indexable(child, source)
21545                        && cpp_sentinel_macro_region(child, source).is_none()
21546                    {
21547                        return false;
21548                    }
21549                    saw_member = true;
21550                }
21551                // The attribute a string-argument macro leaves as a call statement
21552                // of its own. It declares nothing; the member it decorated is the
21553                // sibling after it, checked on its own turn.
21554                "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21555                "expression_statement"
21556                    if cpp_reparsed_defaulted_constructor_is_indexable(
21557                        child, &children, class_name, source,
21558                    ) =>
21559                {
21560                    saw_member = true;
21561                }
21562                "expression_statement"
21563                    if class_name.is_some_and(|class_name| {
21564                        cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21565                    }) =>
21566                {
21567                    saw_member = true;
21568                }
21569                "ERROR"
21570                    if (cpp_reparsed_member_error_is_indexable(child)
21571                        || cpp_reparsed_adjacent_copy_control_error(child, source)
21572                        || cpp_reparsed_stranded_member_error(child, source))
21573                        && (child
21574                        .next_named_sibling()
21575                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21576                        || cpp_reparsed_member_error_with_preprocessed_body(child)
21577                        // The macro-declaration grammar folds the member after
21578                        // the copy-control pair into the same ERROR and leaves
21579                        // its body as the following sibling.
21580                        || (cpp_reparsed_adjacent_copy_control_error(child, source)
21581                            && child
21582                                .next_named_sibling()
21583                                .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21584                {
21585                    saw_member = true;
21586                }
21587                "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21588                    saw_member = true;
21589                }
21590                "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21591                    saw_member = true;
21592                }
21593                "expression_statement"
21594                    if cpp_is_stray_semicolon(child, source)
21595                        && child.prev_named_sibling().is_some_and(|error| {
21596                            cpp_reparsed_member_error_is_indexable(error)
21597                                || cpp_reparsed_adjacent_copy_control_error(error, source)
21598                                || cpp_reparsed_stranded_member_error(error, source)
21599                        }) =>
21600                {
21601                    saw_member = true;
21602                }
21603                "compound_statement"
21604                    if cpp_reparsed_constructor_body_is_indexable(child, source)
21605                        || cpp_reparsed_attribute_requires_body(child, source)
21606                        || child.prev_named_sibling().is_some_and(|error| {
21607                            cpp_reparsed_adjacent_copy_control_error(error, source)
21608                        }) =>
21609                {
21610                    saw_member = true;
21611                }
21612                // A copy-control declaration the macro-declaration grammar reads
21613                // as a function-like macro wrapping a type: `XMLElement( const
21614                // XMLElement& )` in a statement-context reparse is a
21615                // `macro_type_specifier`, admitted only where the parse would
21616                // otherwise contain an error. The live `;` beside it is the
21617                // declaration's own terminator, the same proof the ERROR
21618                // envelope's stray semicolon carried.
21619                "macro_type_specifier"
21620                    if child
21621                        .child_by_field_name("name")
21622                        .is_some_and(|name| name.kind() == "identifier")
21623                        && child
21624                            .child_by_field_name("type")
21625                            .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21626                        && child.next_sibling().is_some_and(|sibling| {
21627                            sibling.kind() == ";" && !sibling.is_missing()
21628                        }) =>
21629                {
21630                    saw_member = true;
21631                }
21632                kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21633                _ => return false,
21634            }
21635            index += 1;
21636        }
21637        let Some(region) = regions.pop() else {
21638            return saw_member;
21639        };
21640        let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21641            return false;
21642        };
21643        region_tree = Some(tree);
21644    }
21645}
21646
21647/// Detect the malformed constructor shape that tree-sitter exposes as an
21648/// access-label statement followed by initializer-looking declarations. The
21649/// declarations are not class members: visiting their `location(loc)` and
21650/// `string(s)` function declarators would publish synthetic functions. The
21651/// export-class fallback keeps the original sibling nodes and therefore avoids
21652/// this parser artifact. The returned range identifies the real constructor
21653/// header, which can be reparsed independently as a structured declarator.
21654fn cpp_reparsed_synthetic_initializer_constructor_range(
21655    root: Node<'_>,
21656    class_name: &str,
21657    source: &str,
21658    constructor_end: usize,
21659) -> Option<std::ops::Range<usize>> {
21660    let mut stack = {
21661        let mut cursor = root.walk();
21662        root.named_children(&mut cursor).collect::<Vec<_>>()
21663    };
21664    while let Some(current) = stack.pop() {
21665        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21666            current,
21667            class_name,
21668            source,
21669            constructor_end,
21670        ) {
21671            return Some(range);
21672        }
21673        if current.kind() == "ERROR" {
21674            let mut cursor = current.walk();
21675            stack.extend(current.named_children(&mut cursor));
21676        }
21677    }
21678    None
21679}
21680
21681/// Recover an inline constructor that a function-like export macro makes
21682/// tree-sitter merge with the following overload. In the reparsed class-body
21683/// region, the access label wraps one declaration whose ERROR contains the
21684/// constructor declarator and its base-initializer/body, while the declaration's
21685/// ordinary declarator is the following overload. Every boundary below comes
21686/// from that CST; no source syntax is reparsed by hand.
21687fn cpp_reparsed_merged_inline_constructor<'tree>(
21688    root: Node<'tree>,
21689    class_name: &str,
21690    source: &str,
21691) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21692    let mut stack = vec![root];
21693    while let Some(current) = stack.pop() {
21694        if current.kind() != "labeled_statement" {
21695            let mut cursor = current.walk();
21696            stack.extend(current.named_children(&mut cursor));
21697            continue;
21698        }
21699        let Some(declaration) = current
21700            .named_children(&mut current.walk())
21701            .find(|child| child.kind() == "declaration")
21702        else {
21703            continue;
21704        };
21705        if declaration
21706            .child_by_field_name("type")
21707            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21708        {
21709            continue;
21710        }
21711        let following = declaration
21712            .child_by_field_name("declarator")
21713            .and_then(extract_function_declarator)
21714            .and_then(cpp_function_declarator_name_node);
21715        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21716            continue;
21717        }
21718        let mut declaration_cursor = declaration.walk();
21719        let Some(error) = declaration
21720            .named_children(&mut declaration_cursor)
21721            .find(|child| child.kind() == "ERROR")
21722        else {
21723            continue;
21724        };
21725        let mut error_cursor = error.walk();
21726        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21727        let Some(constructor) = error_children.iter().copied().find(|child| {
21728            child.kind() == "function_declarator"
21729                && cpp_function_declarator_name_node(*child)
21730                    .is_some_and(|name| node_text(name, source).trim() == class_name)
21731        }) else {
21732            continue;
21733        };
21734        let Some(body) = error_children.iter().copied().find_map(|child| {
21735            (child.kind() == "init_declarator")
21736                .then(|| child.child_by_field_name("value"))
21737                .flatten()
21738                .filter(|value| value.kind() == "initializer_list")
21739        }) else {
21740            continue;
21741        };
21742        if constructor.end_byte() > body.start_byte() {
21743            continue;
21744        }
21745        return Some((constructor.start_byte()..body.end_byte(), body));
21746    }
21747    None
21748}
21749
21750fn cpp_reparsed_synthetic_initializer_constructor(
21751    node: Node<'_>,
21752    class_name: &str,
21753    source: &str,
21754    constructor_end: usize,
21755) -> Option<std::ops::Range<usize>> {
21756    if node.kind() != "labeled_statement" {
21757        return None;
21758    }
21759    let mut cursor = node.walk();
21760    let named = node
21761        .named_children(&mut cursor)
21762        .filter(|child| child.kind() != "comment")
21763        .collect::<Vec<_>>();
21764    let label = named.first()?;
21765    if label.kind() != "statement_identifier"
21766        || !matches!(
21767            node_text(*label, source).trim(),
21768            "public" | "private" | "protected"
21769        )
21770    {
21771        return None;
21772    }
21773    let call_error_index = named.iter().position(|child| {
21774        if child.kind() != "ERROR" {
21775            return false;
21776        }
21777        // The macro-declaration grammar keeps the constructor head's type as
21778        // the ERROR's own type field; the older grammar flattened the head
21779        // into a call expression inside it. ERROR nodes retain field labels
21780        // only by child index in runtime 0.25.
21781        let type_child = (0..child.child_count()).find_map(|index| {
21782            let candidate = child.child(index)?;
21783            (child.field_name_for_child(index as u32) == Some("type")
21784                && candidate.kind() == "type_identifier"
21785                && node_text(candidate, source).trim() == class_name)
21786                .then_some(candidate)
21787        });
21788        if type_child.is_some() {
21789            return true;
21790        }
21791        let mut stack = vec![*child];
21792        while let Some(current) = stack.pop() {
21793            if current.kind() == "call_expression"
21794                && current
21795                    .child_by_field_name("function")
21796                    .is_some_and(|function| {
21797                        function.kind() == "identifier"
21798                            && node_text(function, source).trim() == class_name
21799                    })
21800            {
21801                return true;
21802            }
21803            let mut cursor = current.walk();
21804            stack.extend(current.named_children(&mut cursor));
21805        }
21806        false
21807    })?;
21808    let constructor_start = {
21809        let head = named[call_error_index];
21810        let type_child = (0..head.child_count()).find_map(|index| {
21811            let candidate = head.child(index)?;
21812            (head.field_name_for_child(index as u32) == Some("type")
21813                && candidate.kind() == "type_identifier"
21814                && node_text(candidate, source).trim() == class_name)
21815                .then_some(candidate)
21816        });
21817        if let Some(type_node) = type_child {
21818            type_node.start_byte()
21819        } else {
21820            let mut stack = vec![head];
21821            let mut found = None;
21822            while let Some(current) = stack.pop() {
21823                if current.kind() == "call_expression"
21824                    && current
21825                        .child_by_field_name("function")
21826                        .is_some_and(|function| {
21827                            function.kind() == "identifier"
21828                                && node_text(function, source).trim() == class_name
21829                        })
21830                {
21831                    found = Some(current);
21832                    break;
21833                }
21834                let mut cursor = current.walk();
21835                stack.extend(current.named_children(&mut cursor));
21836            }
21837            found?.start_byte()
21838        }
21839    };
21840    named.iter().skip(call_error_index + 1).find(|child| {
21841        child.kind() == "declaration" && child.has_error() && {
21842            let mut cursor = child.walk();
21843            child.named_children(&mut cursor).any(|declarator| {
21844                declarator.kind() == "init_declarator"
21845                    && declarator
21846                        .child_by_field_name("declarator")
21847                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
21848                    && declarator
21849                        .child_by_field_name("value")
21850                        .is_some_and(|value| value.kind() == "initializer_list")
21851            })
21852        }
21853    })?;
21854    Some(constructor_start..constructor_end)
21855}
21856
21857fn cpp_reparsed_exact_constructor_declarator<'tree>(
21858    root: Node<'tree>,
21859    start: usize,
21860    class_name: &str,
21861    source: &str,
21862) -> Option<Node<'tree>> {
21863    let mut candidate = None;
21864    let mut stack = vec![root];
21865    while let Some(current) = stack.pop() {
21866        if current.kind() == "function_declarator"
21867            && current.start_byte() == start
21868            && cpp_function_declarator_name_node(current)
21869                .is_some_and(|name| node_text(name, source).trim() == class_name)
21870        {
21871            if candidate.is_some() {
21872                return None;
21873            }
21874            candidate = Some(current);
21875            continue;
21876        }
21877        let mut cursor = current.walk();
21878        stack.extend(current.named_children(&mut cursor));
21879    }
21880    candidate
21881}
21882
21883fn cpp_is_indexable_item_kind(kind: &str) -> bool {
21884    matches!(
21885        kind,
21886        "namespace_definition"
21887            | "class_specifier"
21888            | "struct_specifier"
21889            | "union_specifier"
21890            | "enum_specifier"
21891            | "function_definition"
21892            | "template_declaration"
21893            | "declaration"
21894            | "field_declaration"
21895            | "alias_declaration"
21896            | "static_assert_declaration"
21897            | "type_definition"
21898            | "using_declaration"
21899            | "linkage_specification"
21900            | "preproc_def"
21901            | "preproc_function_def"
21902            | "preproc_include"
21903            | "preproc_if"
21904            | "preproc_ifdef"
21905            | "preproc_call"
21906    )
21907}
21908
21909#[cfg(test)]
21910mod tests {
21911
21912    #[test]
21913    fn recovered_callable_grammar_and_original_bytes() {
21914        use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
21915
21916        let parsed = tree(SOURCE);
21917        // These are the current grammar's contracts, including the macro_modifier
21918        // support that has replaced some older ERROR/argument_list recoveries.
21919        // Each row selects a real source region and independently pins its fields
21920        // and the tokens that must survive a synthetic class-context reparse.
21921        let cases = [
21922            (
21923                "expression_statement",
21924                58..72,
21925                &[][..],
21926                &[(58..64, "Widget"), (65..70, "value")][..],
21927            ),
21928            (
21929                "field_declaration",
21930                92..145,
21931                &[
21932                    ("type", "qualified_identifier", 111..122),
21933                    ("declarator", "function_declarator", 123..144),
21934                ][..],
21935                &[
21936                    (123..131, "toString"),
21937                    (132..137, "Yield"),
21938                    (138..143, "yield"),
21939                ][..],
21940            ),
21941            (
21942                "function_definition",
21943                147..191,
21944                &[
21945                    ("type", "qualified_identifier", 151..161),
21946                    ("declarator", "parenthesized_declarator", 161..174),
21947                    ("body", "compound_statement", 189..191),
21948                ][..],
21949                &[
21950                    (155..161, "Widget"),
21951                    (162..167, "Value"),
21952                    (168..173, "value"),
21953                ][..],
21954            ),
21955            (
21956                "field_declaration",
21957                193..296,
21958                &[
21959                    ("type", "macro_type_specifier", 193..222),
21960                    ("declarator", "macro_modifier_declarator", 223..263),
21961                    ("default_value", "initializer_list", 291..293),
21962                ][..],
21963                &[
21964                    (233..239, "Widget"),
21965                    (240..248, "grow_fun"),
21966                    (249..253, "grow"),
21967                ][..],
21968            ),
21969            (
21970                "call_expression",
21971                58..71,
21972                &[
21973                    ("function", "identifier", 58..64),
21974                    ("arguments", "argument_list", 64..71),
21975                ][..],
21976                &[][..],
21977            ),
21978            (
21979                "function_declarator",
21980                123..144,
21981                &[
21982                    ("declarator", "field_identifier", 123..131),
21983                    ("parameters", "parameter_list", 131..144),
21984                ][..],
21985                &[][..],
21986            ),
21987            (
21988                "function_declarator",
21989                233..263,
21990                &[
21991                    ("declarator", "field_identifier", 233..239),
21992                    ("parameters", "parameter_list", 239..254),
21993                ][..],
21994                &[][..],
21995            ),
21996        ];
21997        for (kind, span, fields, tokens) in cases {
21998            let node = at(parsed.root_node(), kind, span.clone());
21999            for (name, child_kind, child_span) in fields {
22000                field(node, name, child_kind, child_span.clone());
22001            }
22002            if tokens.is_empty() {
22003                continue;
22004            }
22005            // Match visit_bare_constructor_expression's documented class context.
22006            // The initializer-list body bounds the initialized-constructor region;
22007            // its recovery field also swallowed the original class close.
22008            let end = node
22009                .child_by_field_name("default_value")
22010                .map_or(span.end, |body| body.end_byte());
22011            const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22012            let parsed = crate::graph::syntax::parse_sentinel(
22013                PREFIX,
22014                crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22015                " };",
22016            );
22017            let (synthetic, reparsed) = (parsed.source, parsed.tree);
22018            for (original_span, text) in tokens {
22019                assert_eq!(&SOURCE[original_span.clone()], *text);
22020                let translated = PREFIX.len() + original_span.start - span.start
22021                    ..PREFIX.len() + original_span.end - span.start;
22022                let leaves: Vec<_> = nodes(reparsed.root_node())
22023                    .into_iter()
22024                    .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22025                    .collect();
22026                assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22027                let token = leaves[0];
22028                assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22029                assert_eq!(
22030                    token.start_byte() - PREFIX.len() + span.start
22031                        ..token.end_byte() - PREFIX.len() + span.start,
22032                    original_span.clone()
22033                );
22034                assert!(SOURCE.is_char_boundary(original_span.start));
22035                assert!(SOURCE.is_char_boundary(original_span.end));
22036            }
22037        }
22038    }
22039
22040    use super::*;
22041    use crate::adapter::parse_cpp_file;
22042    use brokk_bifrost_core::analyzer::parsed_file::{
22043        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22044    };
22045    use brokk_bifrost_test_macros::hardwon;
22046    use std::fmt::Write;
22047
22048    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22049        let tree = crate::diagnostics::parse_cpp_tree(source);
22050        let file = ProjectFile::new(std::env::temp_dir(), name);
22051        parse_cpp_file(&file, source, &tree)
22052    }
22053
22054    #[test]
22055    #[hardwon(
22056        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22057    )]
22058    fn comment_only_enums_have_no_enumerators() {
22059        let parsed =
22060            parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22061        let names: Vec<_> = parsed
22062            .declarations()
22063            .iter()
22064            .map(|unit| unit.fq_name())
22065            .collect();
22066        assert_eq!(names, ["Empty"]);
22067    }
22068
22069    #[test]
22070    #[hardwon(
22071        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22072    )]
22073    fn quicksilver_gnu_attribute_orders() {
22074        for attributes in [
22075            "deprecated, warn_unused_result",
22076            "warn_unused_result, deprecated",
22077        ] {
22078            let source = format!("int value() __attribute__(({attributes}));");
22079            let tree = crate::diagnostics::parse_cpp_tree(&source);
22080            assert!(
22081                !tree.root_node().has_error(),
22082                "{}",
22083                tree.root_node().to_sexp()
22084            );
22085            let declaration = tree.root_node().named_child(0).unwrap();
22086            assert!(
22087                cpp_declaration_has_native_must_use_attribute(declaration, &source),
22088                "{source}: {}",
22089                declaration.to_sexp()
22090            );
22091        }
22092    }
22093
22094    #[test]
22095    #[hardwon(
22096        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22097    )]
22098    fn fragmented_brace_matching_ignores_literal_tokens() {
22099        // Isolate the brace proof from export-head recognition: the damaged
22100        // field list supplies the same container boundary used by recovery.
22101        let source =
22102            r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22103        let tree = crate::diagnostics::parse_cpp_tree(source);
22104        let tokens = crate::grammar_test_support::nodes(tree.root_node());
22105        assert!(
22106            tokens
22107                .iter()
22108                .any(|node| node.kind() == "raw_string_literal")
22109        );
22110        let body = tokens
22111            .iter()
22112            .find(|node| node.kind() == "field_declaration_list")
22113            .unwrap();
22114        let opening = body.child(0).unwrap();
22115        let head = body.named_child(0).unwrap();
22116        assert_eq!(opening.kind(), "{");
22117        assert!(body.has_error());
22118        assert_eq!(
22119            fragmented_container_close_class_body(head, opening, source)
22120                .map(|body| body.reparse_end),
22121            source.rfind('}')
22122        );
22123    }
22124
22125    #[test]
22126    #[hardwon(
22127        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22128    )]
22129    fn quicksilver_uppercase_attributed_classes() {
22130        for (name, body) in [
22131            ("Box", "{ int value; }"),
22132            ("BOX", "{ int value; }"),
22133            ("API", ""),
22134        ] {
22135            let source = format!("class [[deprecated]] {name} {body};");
22136            let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22137            let names = parsed
22138                .declarations()
22139                .iter()
22140                .filter(|unit| unit.is_class())
22141                .map(|unit| unit.identifier().to_owned())
22142                .collect::<Vec<_>>();
22143            assert_eq!(names, [name], "{source}");
22144        }
22145    }
22146
22147    #[test]
22148    #[hardwon(
22149        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22150    )]
22151    fn quicksilver_fragmented_export_uppercase_classes() {
22152        for name in ["Box", "BOX"] {
22153            let source = r#"
22154namespace api {
22155
22156/**
22157* Doc comment
22158*/
22159class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22160   public:
22161      /**
22162      * Construct from a point.
22163      */
22164      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22165
22166#if defined(PROJECT_HAS_LEGACY_POINT)
22167      /**
22168      * Construct from a legacy point.
22169      */
22170      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22171#endif
22172
22173      std::string algo_name() const override;
22174
22175      AlgorithmIdentifier algorithm_identifier() const override;
22176};
22177
22178}
22179"#
22180            .replace("VirtualBased", name);
22181            let tree = crate::diagnostics::parse_cpp_tree(&source);
22182            let mut candidate = None;
22183            walk_named_tree_preorder(tree.root_node(), true, |node| {
22184                let children = node.children(&mut node.walk()).collect::<Vec<_>>();
22185                if let Some(recovered) =
22186                    fragmented_export_macro_class_body(node, &children, &source)
22187                {
22188                    candidate = Some(recovered.name);
22189                    return WalkControl::Break;
22190                }
22191                WalkControl::Continue
22192            });
22193            assert_eq!(
22194                candidate.as_deref(),
22195                Some(name),
22196                "{source}: {}",
22197                tree.root_node().to_sexp()
22198            );
22199        }
22200    }
22201
22202    #[test]
22203    #[hardwon(
22204        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22205    )]
22206    fn quicksilver_constructor_search_skips_other_labels() {
22207        let source = "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n Lookup_Error(std::string_view type, std::string_view algo);\nprivate: do_work();\n";
22208        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22209        let (range, body) =
22210            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22211                .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22212        assert_eq!(
22213            &source[range],
22214            "Lookup_Error(std::string_view err) : Exception(err) {}"
22215        );
22216        assert_eq!(node_text(body, source), "{}");
22217    }
22218
22219    #[test]
22220    #[hardwon(
22221        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22222    )]
22223    fn quicksilver_member_gate_checks_label_payload() {
22224        for (source, expected) in [
22225            ("public: int value;", true),
22226            (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22227            (
22228                "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22229                true,
22230            ),
22231            ("public: do_work();", false),
22232            ("ordinary: int value;", false),
22233        ] {
22234            let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22235            assert_eq!(
22236                cpp_reparsed_members_are_indexable(tree.root_node(), source),
22237                expected,
22238                "{source}: {}",
22239                tree.root_node().to_sexp()
22240            );
22241        }
22242    }
22243
22244    #[test]
22245    #[hardwon(
22246        "db00de0653f1: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22247    )]
22248    fn quicksilver_sentinel_uppercase_classes() {
22249        for name in ["Box", "BOX"] {
22250            let source = format!(
22251                "namespace absl {{\nABSL_NAMESPACE_BEGIN namespace container_internal {{\ntemplate <typename T> class {name} {{\npublic:\nusing value_type = T;\nT operator->() const {{ return &operator*(); }}\nusing alias = value_type;\n}};\n}}\n}}\n"
22252            );
22253            let tree = crate::diagnostics::parse_cpp_tree(&source);
22254            let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22255            assert!(
22256                recovered
22257                    .iter()
22258                    .any(|class| class.scope_components == ["absl", "container_internal", name]),
22259                "{source}: {recovered:#?}"
22260            );
22261        }
22262    }
22263
22264    #[test]
22265    fn native_must_use_attribute_nodes_are_structural() {
22266        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22267        let tree = crate::diagnostics::parse_cpp_tree(source);
22268        let root = tree.root_node();
22269        let mut declarations = Vec::new();
22270        walk_named_tree_preorder(root, true, |node| {
22271            if matches!(
22272                node.kind(),
22273                "declaration" | "function_definition" | "field_declaration"
22274            ) {
22275                declarations.push(node);
22276            }
22277            WalkControl::Continue
22278        });
22279        assert_eq!(
22280            declarations
22281                .into_iter()
22282                .map(|declaration| {
22283                    cpp_declaration_has_native_must_use_attribute(declaration, source)
22284                })
22285                .collect::<Vec<_>>(),
22286            vec![true, false, true]
22287        );
22288        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22289        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22290        let mut enum_specifier = None;
22291        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22292            if node.kind() == "enum_specifier" {
22293                enum_specifier = Some(node);
22294                WalkControl::Break
22295            } else {
22296                WalkControl::Continue
22297            }
22298        });
22299        assert!(cpp_declaration_has_native_must_use_attribute(
22300            enum_specifier.expect("enum declaration"),
22301            enum_source
22302        ));
22303    }
22304
22305    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22306        let mut rows = parsed
22307            .signature_metadata
22308            .iter()
22309            .flat_map(|(unit, entries)| {
22310                entries.iter().map(move |metadata| {
22311                    assert!(
22312                        metadata.callable_modifiers_recorded(),
22313                        "{} must record that the walk read its declaration shape",
22314                        unit.fq_name()
22315                    );
22316                    format!(
22317                        "{} decl_only={} static={} constructor={} visibility={:?}",
22318                        unit.fq_name(),
22319                        metadata.is_declaration_only(),
22320                        metadata.callable_is_static(),
22321                        metadata.callable_is_constructor(),
22322                        metadata
22323                            .callable_declared_visibility()
22324                            .expect("a recorded modifier set states a visibility"),
22325                    )
22326                })
22327            })
22328            .collect::<Vec<_>>();
22329        rows.sort();
22330        rows
22331    }
22332
22333    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
22334    /// exactly the spelling it was given, with the nested-class `$` recorded as
22335    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
22336    #[test]
22337    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22338    fn rendered_name_fq_round_trips_nested_class_spellings() {
22339        assert!(cpp_rendered_name_fq("").is_empty());
22340        for rendered in [
22341            "Only",
22342            "Top$Only",
22343            "ns.Outer$Inner",
22344            "Top$Only.b",
22345            "ns.Outer$Inner.a",
22346            "Buck$Stop",
22347        ] {
22348            let fq = cpp_rendered_name_fq(rendered);
22349            assert_eq!(
22350                fq.display_native(Language::Cpp, segment_interner()),
22351                rendered,
22352                "native round trip for {rendered}"
22353            );
22354        }
22355        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22356            .segments()
22357            .iter()
22358            .map(|segment| segment_interner().resolve(*segment).1)
22359            .collect();
22360        assert_eq!(
22361            kinds,
22362            [
22363                SegmentKind::Unknown,
22364                SegmentKind::Unknown,
22365                SegmentKind::Nested
22366            ]
22367        );
22368    }
22369
22370    /// #3631: a `::`-only source spelling must name the nested `$` identity
22371    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
22372    /// the single rendered-name parse.
22373    #[test]
22374    #[hardwon(
22375        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22376    )]
22377    fn colon_source_aliases_name_nested_and_namespace_identities() {
22378        let native = |rendered: &str| {
22379            cpp_colon_source_aliases(rendered)
22380                .into_iter()
22381                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22382                .collect::<Vec<_>>()
22383        };
22384        let top = native("Top::Only");
22385        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22386        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22387        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22388        let nested = native("ns::Outer::Inner");
22389        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22390        assert!(
22391            nested.contains(&"ns::Outer.Inner".to_string()),
22392            "{nested:?}"
22393        );
22394        let member = native("Top::Only::b");
22395        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22396        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22397        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22398        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22399        assert!(cpp_colon_source_aliases("Only").is_empty());
22400        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22401            fq.segments()
22402                .iter()
22403                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22404        }));
22405    }
22406
22407    #[test]
22408    #[hardwon(
22409        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22410    )]
22411    fn displaced_terminator_ignores_nested_initializer_endif() {
22412        let source = "#ifdef ENABLE_ITEMS\n\
22413struct Item { int reg; };\n\
22414static const struct Item items[] = {{1}};\n\
22415static const struct Item extras[] = {\n\
22416  {0},\n\
22417#ifndef REDUCED\n\
22418  {1},\n\
22419#endif\n\
22420};\n\
22421int read_item(int i) { return items[i].reg; }\n\
22422#endif\n";
22423        let mut parser = tree_sitter::Parser::new();
22424        parser
22425            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22426            .expect("C++ grammar");
22427        let tree = parser.parse(source, None).expect("fixture tree");
22428        let conditional = tree
22429            .root_node()
22430            .named_child(0)
22431            .filter(|node| node.kind() == "preproc_ifdef")
22432            .expect("outer conditional");
22433
22434        assert!(conditional.has_error());
22435        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22436        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22437    }
22438
22439    #[test]
22440    #[hardwon(
22441        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22442    )]
22443    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22444        let source = "struct Image { PyObject_HEAD Imaging image; };";
22445        let parsed = parse_cpp_declarations(source, "image.h");
22446        let names = parsed
22447            .declarations()
22448            .iter()
22449            .map(|unit| unit.fq_name())
22450            .collect::<Vec<_>>();
22451
22452        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22453        assert!(
22454            names.iter().all(|name| name != "Image.Imaging"),
22455            "the pseudo-declarator must not become a field: {names:#?}"
22456        );
22457
22458        let pointer = parse_cpp_declarations(
22459            "struct Image { PyObject_HEAD Imaging *image; };",
22460            "image-pointer.h",
22461        );
22462        let pointer_names = pointer
22463            .declarations()
22464            .iter()
22465            .map(|unit| unit.fq_name())
22466            .collect::<Vec<_>>();
22467        assert!(
22468            pointer_names.iter().any(|name| name == "Image.image"),
22469            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22470        );
22471        assert!(
22472            pointer_names.iter().all(|name| name != "Image.Imaging"),
22473            "the pointer recovery error must not become a field: {pointer_names:#?}"
22474        );
22475
22476        let unrelated_macro = parse_cpp_declarations(
22477            "struct Image { OTHER_HEAD Imaging other; };",
22478            "image-near-miss.h",
22479        );
22480        let unrelated_names = unrelated_macro
22481            .declarations()
22482            .iter()
22483            .map(|unit| unit.fq_name())
22484            .collect::<Vec<_>>();
22485        assert!(
22486            unrelated_names.iter().any(|name| name == "Image.other"),
22487            "an object-like macro in specifier position is a `macro_modifier`, so the \
22488             member is the structured best-effort reading, the same one the \
22489             macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22490        );
22491        assert!(
22492            unrelated_names
22493                .iter()
22494                .all(|name| name != "Image.OTHER_HEAD"),
22495            "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22496        );
22497    }
22498
22499    #[test]
22500    #[hardwon(
22501        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22502    )]
22503    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22504        let source = r#"namespace testing {
22505namespace internal {
22506    namespace detail {
22507        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22508        public:
22509            int value() const { return count_ + 1; }
22510        private:
22511            int count_;
22512        };
22513        class GTEST_API_ [[nodiscard]] OtherReporter {
22514        public:
22515            int value() const { return count_ + 2; }
22516        private:
22517            int count_;
22518        };
22519    }
22520
22521    template <typename T>
22522    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22523
22524    class TailReporter {};
22525}
22526}
22527"#;
22528        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22529        let declarations = parsed.declarations();
22530        let mut parser = tree_sitter::Parser::new();
22531        parser
22532            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22533            .unwrap();
22534        let tree = parser.parse(source, None).unwrap();
22535        let tail_start = source.find("TailReporter").expect("tail class");
22536        let tail_node = tree
22537            .root_node()
22538            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22539            .expect("tail class AST node");
22540        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22541        assert!(
22542            tree.root_node().has_error(),
22543            "the malformed class must exercise recovery"
22544        );
22545        assert!(index.region_at(tail_start).is_some());
22546        assert_eq!(
22547            index.enclosing_namespace_components(tail_node, source),
22548            ["testing", "internal"]
22549        );
22550        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22551        let mut recovered_parsed = ParsedFile::new(String::new());
22552        let class_unit = CodeUnit::new_fq(
22553            file.clone(),
22554            CodeUnitType::Class,
22555            "testing",
22556            "ScopedFakeTestPartResultReporter",
22557            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22558        );
22559        let scope = ScopeInfo {
22560            package_name: "testing".to_string(),
22561            module: None,
22562            class_unit: Some(class_unit),
22563            template_signature: Some("<typename T>".to_string()),
22564            template_metadata: Some(CppTemplateMetadata {
22565                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22566                primary_fq_name: String::new(),
22567                parameters: Vec::new(),
22568                specialization_arguments: Vec::new(),
22569                alias_target: None,
22570            }),
22571            declarations_are_fields: true,
22572            recovered_specialization_member_scope: true,
22573            visible_using_namespaces: Vec::new(),
22574            visible_using_declarations: Arc::default(),
22575        };
22576        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22577        let properties = RefCell::new(HashMap::default());
22578        let dependencies =
22579            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22580        let ancestry = ParentIndex::new(tree.root_node());
22581        let mut visitor = CppVisitor {
22582            file: &file,
22583            source,
22584            parsed: &mut recovered_parsed,
22585            source_collector: &collector,
22586            source_properties: &properties,
22587            source_dependencies: &dependencies,
22588            primary_ancestry: &ancestry,
22589            primary_root_id: tree.root_node().id(),
22590            source_links: HashMap::default(),
22591            source_declarations: HashSet::default(),
22592            preprocessor_context_cache: Default::default(),
22593            active_source_context: None,
22594            c_tag_semantics: false,
22595            c_tag_scope_witness: false,
22596            recovered_class_sibling_scopes: HashMap::default(),
22597            consumed_fragment_regions: Vec::new(),
22598            orphaned_namespaces: &index,
22599            partitioned_regions: Vec::new(),
22600            namespace_forward_scans: HashMap::default(),
22601            recovery_captures: Vec::new(),
22602            object_macro_fields: HashMap::default(),
22603            ambiguous_object_macro_fields: HashSet::default(),
22604        };
22605        let recovered = visitor
22606            .recovered_namespace_scope(tail_node, &scope)
22607            .expect("the tail must use the stolen namespace scope");
22608        assert_eq!(recovered.package_name, "testing::internal");
22609        assert!(
22610            recovered.class_unit.is_none(),
22611            "recovered namespace declarations cannot retain the malformed class owner"
22612        );
22613        assert!(recovered.template_signature.is_none());
22614        assert!(recovered.template_metadata.is_none());
22615        assert!(!recovered.declarations_are_fields);
22616        assert!(!recovered.recovered_specialization_member_scope);
22617        assert!(
22618            declarations
22619                .iter()
22620                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22621            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22622        );
22623        assert!(
22624            declarations
22625                .iter()
22626                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22627            "the recovered free function remains in its namespace: {declarations:#?}"
22628        );
22629        assert!(
22630            declarations
22631                .iter()
22632                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22633            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22634        );
22635        assert!(
22636            declarations.iter().all(|unit| {
22637                !unit
22638                    .short_name()
22639                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22640            }),
22641            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22642        );
22643        assert!(
22644            declarations
22645                .iter()
22646                .all(|unit| !unit.identifier().is_empty()),
22647            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22648        );
22649    }
22650
22651    /// #4292: a recovered namespace level whose close the brace stack could not
22652    /// prove keeps its head-only range, and the first declaration the walk
22653    /// reaches inside the region is not the namespace. The Module's name
22654    /// occurrence must be the level's own name token; deriving it from the
22655    /// walk's current node paired the head range with an unrelated name and
22656    /// panicked the collector's containment assertion.
22657    #[test]
22658    fn recovered_namespace_levels_name_their_own_name_tokens() {
22659        // #3084's collapsed-head fixture without the namespace's own close:
22660        // the file ends inside the namespace, so the brace stack proves no
22661        // close for its `{` and the level keeps its `namespace Name {` head
22662        // alone (#3328).
22663        let source = r#"namespace outer::app {
22664
22665    class Target {
22666        int value_;
22667    };
22668
22669    template <typename T>
22670    class Holder {
22671    public:
22672        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
22673
22674#define HOLDER_DEFINE_OP( id, op )                                             \
22675    template <typename U>                                                      \
22676    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
22677        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
22678        return Target{};                                                       \
22679    }
22680
22681        HOLDER_DEFINE_OP( equal, == )
22682#undef HOLDER_DEFINE_OP
22683        T m_lhs;
22684    };
22685
22686    class Tail {};
22687"#;
22688        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
22689        let mut parser = tree_sitter::Parser::new();
22690        parser
22691            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22692            .unwrap();
22693        let tree = parser.parse(source, None).unwrap();
22694        assert!(
22695            tree.root_node().has_error(),
22696            "the fixture must reproduce the parse damage"
22697        );
22698        let target_start = source.find("class Target").expect("target class");
22699        let target_node = tree
22700            .root_node()
22701            .named_descendant_for_byte_range(target_start, target_start + "class".len())
22702            .expect("target class AST node");
22703        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22704        // The macro-declaration grammar keeps the namespace head and its
22705        // members inside a surviving `namespace_definition`; the missing close
22706        // no longer collapses the head into an ERROR, so the scope authority
22707        // answers from parsed ancestry rather than a recovered region.
22708        assert_eq!(
22709            index.enclosing_namespace_components(target_node, source),
22710            ["outer", "app"]
22711        );
22712        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
22713        let mut recovered_parsed = ParsedFile::new(String::new());
22714        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22715        let properties = RefCell::new(HashMap::default());
22716        let dependencies =
22717            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22718        let ancestry = ParentIndex::new(tree.root_node());
22719        let mut visitor = CppVisitor {
22720            file: &file,
22721            source,
22722            parsed: &mut recovered_parsed,
22723            source_collector: &collector,
22724            source_properties: &properties,
22725            source_dependencies: &dependencies,
22726            primary_ancestry: &ancestry,
22727            primary_root_id: tree.root_node().id(),
22728            source_links: HashMap::default(),
22729            source_declarations: HashSet::default(),
22730            preprocessor_context_cache: Default::default(),
22731            active_source_context: None,
22732            c_tag_semantics: false,
22733            c_tag_scope_witness: false,
22734            recovered_class_sibling_scopes: HashMap::default(),
22735            consumed_fragment_regions: Vec::new(),
22736            orphaned_namespaces: &index,
22737            partitioned_regions: Vec::new(),
22738            namespace_forward_scans: HashMap::default(),
22739            recovery_captures: Vec::new(),
22740            object_macro_fields: HashMap::default(),
22741            ambiguous_object_macro_fields: HashSet::default(),
22742        };
22743        // The surviving `namespace_definition` takes the ordinary namespace
22744        // visit, which declares each level with its own name token.
22745        visitor.visit_container(
22746            tree.root_node(),
22747            &ancestry,
22748            "",
22749            None,
22750            None,
22751            None,
22752            Vec::new(),
22753            Arc::default(),
22754        );
22755        let collector = collector.borrow();
22756        for (module_name, expected_name) in [
22757            (
22758                "outer",
22759                Range {
22760                    start_byte: 10,
22761                    end_byte: 15,
22762                    start_line: 1,
22763                    end_line: 1,
22764                },
22765            ),
22766            (
22767                "outer::app",
22768                Range {
22769                    start_byte: 17,
22770                    end_byte: 20,
22771                    start_line: 1,
22772                    end_line: 1,
22773                },
22774            ),
22775        ] {
22776            let declaration = visitor
22777                .source_links
22778                .iter()
22779                .find(|(unit, _)| unit.fq_name() == module_name)
22780                .expect("the recovered module links a source declaration")
22781                .1
22782                .last()
22783                .expect("the recovered module declaration");
22784            let declaration = collector.declaration(*declaration);
22785            let name = declaration
22786                .name
22787                .expect("the recovered module records its name occurrence");
22788            assert_eq!(
22789                collector.occurrence(name).range,
22790                expected_name,
22791                "the recovered module names its own token: {module_name}"
22792            );
22793        }
22794        assert!(
22795            parsed.declarations().iter().any(|unit| {
22796                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
22797            }),
22798            "the recovered namespace members keep their namespace: {:#?}",
22799            parsed.declarations()
22800        );
22801    }
22802
22803    #[test]
22804    #[hardwon(
22805        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22806    )]
22807    fn object_like_field_macros_materialize_owner_specific_declarations() {
22808        let source = r#"#define PUBLIC_FIELDS int public_value;
22809#define PRIVATE_FIELDS int private_value;
22810#define NOT_A_FIELD_LIST not a declaration
22811
22812struct First {
22813  PUBLIC_FIELDS
22814  PRIVATE_FIELDS
22815};
22816struct Second {
22817  PUBLIC_FIELDS
22818  NOT_A_FIELD_LIST
22819};
22820#undef PUBLIC_FIELDS
22821struct Third {
22822  PUBLIC_FIELDS
22823};
22824"#;
22825        let parsed = parse_cpp_declarations(source, "macro-fields.c");
22826        let fields = parsed
22827            .declarations()
22828            .iter()
22829            .filter(|unit| unit.is_field())
22830            .map(|unit| unit.fq_name())
22831            .collect::<Vec<_>>();
22832
22833        assert!(
22834            fields.contains(&"First.public_value".to_string()),
22835            "{fields:?}"
22836        );
22837        assert!(
22838            fields.contains(&"First.private_value".to_string()),
22839            "{fields:?}"
22840        );
22841        assert!(
22842            fields.contains(&"Second.public_value".to_string()),
22843            "{fields:?}"
22844        );
22845        assert!(
22846            !fields.iter().any(|field| field.contains("not_a_field")),
22847            "malformed macro must fail closed: {fields:?}"
22848        );
22849        assert!(
22850            !fields.iter().any(|field| field.starts_with("Third.")),
22851            "undefined macro must fail closed: {fields:?}"
22852        );
22853    }
22854
22855    #[test]
22856    #[hardwon(
22857        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22858    )]
22859    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
22860        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
22861        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
22862        let mut macros = parsed
22863            .declarations()
22864            .iter()
22865            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
22866            .collect::<Vec<_>>();
22867        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
22868
22869        assert_eq!(macros.len(), 2, "{macros:#?}");
22870        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
22871        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
22872        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
22873        assert_eq!(
22874            parsed.declaration_ranges(macros[1])[0].start_byte,
22875            source.rfind("#define VALUE 2").expect("second definition")
22876        );
22877    }
22878
22879    #[test]
22880    #[hardwon(
22881        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
22882    )]
22883    fn identifies_export_macro_class_base_displaced_into_declarator() {
22884        let source = r#"#define PROJECT_API_
22885namespace project {
22886namespace internal {
22887template <typename T>
22888class Base {};
22889}
22890template <typename T>
22891class Wrapper;
22892template <>
22893class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
22894}
22895"#;
22896        let mut parser = tree_sitter::Parser::new();
22897        parser
22898            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22899            .unwrap();
22900        let tree = parser.parse(source, None).unwrap();
22901        let start = source.find("internal::Base<int>").expect("base");
22902        let mut base = tree
22903            .root_node()
22904            .descendant_for_byte_range(start, start + 8)
22905            .expect("base syntax");
22906        while base.kind() != "qualified_identifier" {
22907            base = base.parent().expect("qualified base ancestor");
22908        }
22909        assert!(
22910            is_recovered_exported_class_base_type_node(base, source),
22911            "{}",
22912            tree.root_node().to_sexp()
22913        );
22914    }
22915
22916    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
22917        parsed
22918            .declarations()
22919            .iter()
22920            .filter(|unit| unit.is_function())
22921            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
22922            .collect()
22923    }
22924
22925    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
22926        let mut identities = all_function_identities(parsed)
22927            .into_iter()
22928            .map(|(name, signature)| (name, signature.unwrap_or_default()))
22929            .collect::<Vec<_>>();
22930        identities.sort();
22931        identities
22932    }
22933
22934    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
22935    /// prototype macros. Each case is parsed independently so a preceding
22936    /// malformed line cannot flatten the next declaration's fields into one
22937    /// cascading `ERROR`; that shape has no structural proof and must fail
22938    /// closed rather than fall back to a token scan. Parameter names are
22939    /// dropped from signatures just as they are for ordinary C prototypes.
22940    #[test]
22941    #[hardwon(
22942        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
22943    )]
22944    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
22945        let cases = [
22946            (
22947                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
22948                "pg_typemap_fit_to_result",
22949                "(VALUE, VALUE)",
22950            ),
22951            (
22952                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
22953                "pg_typemap_result_value",
22954                "(t_typemap *, VALUE, int, int)",
22955            ),
22956            (
22957                "void pg_typemap_mark _(( void * ));",
22958                "pg_typemap_mark",
22959                "(void *)",
22960            ),
22961            (
22962                "void init_pg_type_map _(( void ));",
22963                "init_pg_type_map",
22964                "(void)",
22965            ),
22966            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
22967            (
22968                "extern VALUE pg_extern _(( VALUE, VALUE ));",
22969                "pg_extern",
22970                "(VALUE, VALUE)",
22971            ),
22972            (
22973                "size_t pg_typemap_memsize _(( const void * ));",
22974                "pg_typemap_memsize",
22975                "(const void *)",
22976            ),
22977            (
22978                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
22979                "pg_wrap_socket_io",
22980                "(int, VALUE, VALUE *, int *)",
22981            ),
22982            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
22983            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
22984            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
22985        ];
22986
22987        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
22988            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
22989            assert_eq!(
22990                function_identities(&parsed),
22991                vec![(name.to_string(), signature.to_string())],
22992                "{source}: {:#?}",
22993                parsed.declarations()
22994            );
22995            assert!(
22996                parsed.declarations().iter().all(|unit| !unit.is_field()),
22997                "{source} must not retain the malformed field: {:#?}",
22998                parsed.declarations()
22999            );
23000        }
23001    }
23002
23003    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
23004    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
23005    /// it as a multiplication of a type by a call
23006    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
23007    /// wrapped in an `expression_statement` that `has_error()` only because
23008    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
23009    /// here with a clean preceding field so the prototype remains the
23010    /// parser-owned `expression_statement` this recovery requires.
23011    #[test]
23012    #[hardwon(
23013        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23014    )]
23015    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23016        let cases = [
23017            (
23018                "PGconn *pg_get_pgconn _(( VALUE ));",
23019                "pg_get_pgconn",
23020                "(VALUE)",
23021            ),
23022            (
23023                "PGresult* pgresult_get _(( VALUE ));",
23024                "pgresult_get",
23025                "(VALUE)",
23026            ),
23027        ];
23028        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23029            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23030            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23031            assert_eq!(
23032                function_identities(&parsed),
23033                vec![(name.to_string(), signature.to_string())],
23034                "{source}: {:#?}",
23035                parsed.declarations()
23036            );
23037            assert_eq!(
23038                parsed
23039                    .declarations()
23040                    .iter()
23041                    .filter(|unit| unit.is_field())
23042                    .map(|unit| unit.fq_name())
23043                    .collect::<Vec<_>>(),
23044                vec!["rb_mPG".to_string()],
23045                "{source}: {:#?}",
23046                parsed.declarations()
23047            );
23048        }
23049    }
23050
23051    /// #6902. A class-body prototype macro line is a `field_declaration`. It
23052    /// takes the same candidate gate and reparse as a file-scope one, inside
23053    /// the class envelope, so the member keeps its owner, its written range,
23054    /// its `static`, and the access its label states.
23055    #[test]
23056    #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23057    fn c_prototype_macro_recovers_class_body_members() {
23058        const SOURCE: &str = concat!(
23059            "class Pg {\n",
23060            "  VALUE wrap _(( VALUE self ));\n",
23061            "public:\n",
23062            "  int invoke __P((int value));\n",
23063            "  static char *name PROTO(( int index ));\n",
23064            "};\n",
23065        );
23066        let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23067        assert_eq!(
23068            function_identities(&parsed),
23069            vec![
23070                ("Pg.invoke".to_string(), "(int)".to_string()),
23071                ("Pg.name".to_string(), "(int)".to_string()),
23072                ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23073            ],
23074            "{:#?}",
23075            parsed.declarations()
23076        );
23077        assert!(
23078            parsed.declarations().iter().all(|unit| !unit.is_field()),
23079            "the malformed fields must not survive: {:#?}",
23080            parsed.declarations()
23081        );
23082        let class = parsed
23083            .declarations()
23084            .iter()
23085            .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23086            .expect("class Pg");
23087        let mut children = parsed
23088            .children
23089            .get(class)
23090            .into_iter()
23091            .flatten()
23092            .map(|unit| unit.fq_name())
23093            .collect::<Vec<_>>();
23094        children.sort();
23095        assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23096        for (name, written) in [
23097            ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23098            ("Pg.invoke", "int invoke __P((int value));"),
23099            ("Pg.name", "static char *name PROTO(( int index ));"),
23100        ] {
23101            let unit = parsed
23102                .declarations()
23103                .iter()
23104                .find(|unit| unit.is_function() && unit.fq_name() == name)
23105                .unwrap_or_else(|| panic!("missing recovered member {name}"));
23106            let [range] = parsed.declaration_ranges(unit) else {
23107                panic!("{name} must have exactly one range");
23108            };
23109            assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23110        }
23111        assert_eq!(
23112            recorded_callable_modifiers(&parsed),
23113            vec![
23114                "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23115                    .to_string(),
23116                "Pg.name decl_only=true static=true constructor=false visibility=Public"
23117                    .to_string(),
23118                "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23119                    .to_string(),
23120            ],
23121        );
23122    }
23123
23124    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
23125    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
23126    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
23127    /// regions. The witnessed declaration must still become a Function and
23128    /// its type must not survive as the name of a bogus Field. This test does
23129    /// not claim that structurally flattened neighboring prototypes are safe
23130    /// to recover.
23131    #[test]
23132    #[hardwon(
23133        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23134    )]
23135    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23136        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
23137VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
23138int pg_typemap_fit_to_copy_get                         _(( VALUE ));
23139VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
23140t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
23141VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
23142void pg_typemap_mark                                   _(( void * ));
23143size_t pg_typemap_memsize                              _(( const void * ));
23144void pg_typemap_compact                                _(( void * ));
23145
23146PGconn *pg_get_pgconn                                  _(( VALUE ));
23147t_pg_connection *pg_get_connection                     _(( VALUE ));
23148VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
23149#ifdef __GNUC__
23150__attribute__((format(printf, 3, 4)))
23151#endif
23152NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
23153VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23154void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23155
23156
23157VALUE pg_new_result                                    _(( PGresult *, VALUE ));
23158VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
23159PGresult* pgresult_get                                 _(( VALUE ));
23160VALUE pg_result_check                                  _(( VALUE ));
23161VALUE pg_result_clear                                  _(( VALUE ));
23162VALUE pg_tuple_new                                     _(( VALUE, int ));
23163
23164/*
23165 * Fetch the data pointer for the result object
23166 */
23167static inline t_pg_result *
23168pgresult_get_this( VALUE self )
23169{
23170	return RTYPEDDATA_DATA(self);
23171}
23172
23173
23174rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
23175const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
23176rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
23177
23178"#;
23179        let parsed = parse_cpp_declarations(source, "pg.h");
23180        assert!(
23181            function_identities(&parsed)
23182                .iter()
23183                .any(|(name, signature)| name == "pg_typemap_result_value"
23184                    && signature == "(t_typemap *, VALUE, int, int)"),
23185            "the real issue witness must be a Function with its C signature: {:#?}",
23186            parsed.declarations()
23187        );
23188        assert!(
23189            parsed
23190                .declarations()
23191                .iter()
23192                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23193            "the issue witness must not leave its return type as a Field name: {:#?}",
23194            parsed.declarations()
23195        );
23196    }
23197
23198    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
23199    /// between `explicit` and a constructor name onto the name, producing a
23200    /// `qualified_identifier` whose `::` it had to invent. The declared member
23201    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
23202    /// macro in type position, was already recovered and is the control that
23203    /// both spellings agree.
23204    #[test]
23205    #[hardwon(
23206        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23207    )]
23208    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23209        let source = r#"class SIMD_4x26 final {
23210   public:
23211      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23212      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23213      int m_v;
23214};
23215"#;
23216        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23217        assert_eq!(
23218            function_identities(&parsed),
23219            vec![
23220                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23221                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23222            ],
23223            "{:#?}",
23224            parsed.declarations()
23225        );
23226    }
23227
23228    #[test]
23229    #[hardwon(
23230        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23231    )]
23232    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23233        let source = r#"struct cpu_spec {};
23234static struct cpu_spec __initdata cpu_specs[];
23235static struct cpu_spec ns::cpu_specs[];
23236"#;
23237        let tree = crate::diagnostics::parse_cpp_tree(source);
23238        let root = tree.root_node();
23239        let declarations = root
23240            .named_children(&mut root.walk())
23241            .filter(|node| node.kind() == "declaration")
23242            .collect::<Vec<_>>();
23243        let names = declarations
23244            .iter()
23245            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23246            .filter_map(|declarator| extract_variable_name(declarator, source))
23247            .collect::<Vec<_>>();
23248        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23249    }
23250
23251    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
23252    /// `ERROR` holding the wrapped declaration and every declaration after it
23253    /// until the parser recovers. All of them must be indexed, each with the
23254    /// byte range that spells it.
23255    #[test]
23256    #[hardwon(
23257        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23258    )]
23259    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23260        let source = r#"#include <cstdint>
23261struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23262    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23263                     struct llama_model * model,
23264              struct llama_context_params   params),
23265            "use llama_init_from_model instead");
23266    LLAMA_API int32_t llama_tokenize(
23267        const struct llama_vocab * vocab,
23268                      const char * text,
23269                            bool   parse_special);
23270    LLAMA_API int32_t llama_other(int a);
23271"#;
23272        let parsed = parse_cpp_declarations(source, "llama.h");
23273        assert_eq!(
23274            function_identities(&parsed),
23275            vec![
23276                (
23277                    "llama_new_context_with_model".to_string(),
23278                    "(struct llama_model *, struct llama_context_params)".to_string()
23279                ),
23280                ("llama_other".to_string(), "(int)".to_string()),
23281                (
23282                    "llama_tokenize".to_string(),
23283                    "(const struct llama_vocab *, const char *, bool)".to_string()
23284                ),
23285            ],
23286            "{:#?}",
23287            parsed.declarations()
23288        );
23289
23290        // Each recovered declaration owns the source that spells it, so
23291        // navigation lands on the declaration and not on the macro envelope.
23292        for (name, expected) in [
23293            (
23294                "llama_new_context_with_model",
23295                "LLAMA_API struct llama_context * llama_new_context_with_model(",
23296            ),
23297            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23298            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23299        ] {
23300            let unit = parsed
23301                .declarations()
23302                .iter()
23303                .find(|unit| unit.is_function() && unit.fq_name() == name)
23304                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23305            let [range] = parsed.declaration_ranges(unit) else {
23306                panic!("{name} must have exactly one range");
23307            };
23308            let text = &source[range.start_byte..range.end_byte];
23309            assert!(
23310                text.starts_with(expected),
23311                "{name} range is {text:?}, expected it to start with {expected:?}"
23312            );
23313            assert!(
23314                text.ends_with(')') || text.ends_with(';'),
23315                "{name}: {text:?}"
23316            );
23317        }
23318    }
23319
23320    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
23321    /// invocation's `(` at the end of its line. The macro-declaration grammar
23322    /// keeps each invocation in its own declaration-scope `ERROR`: the wrapped
23323    /// declaration and the hint's exploded words sit in the parameter list,
23324    /// the invocation's `)` closes that list, and its `;` is left to the
23325    /// sibling `expression_statement`. (Before the macro-declaration grammar,
23326    /// the second invocation's envelope swallowed the third into a run of bare
23327    /// siblings; #3094 read that flattening from the token order, and the
23328    /// collapsed-run recovery still owns the envelope/statement pairing the
23329    /// same way.)
23330    ///
23331    /// The identifiers are `library_*` at whisper's own lengths on purpose:
23332    /// tree-sitter prices error recovery partly by how much input a repair
23333    /// skips, and the same construct written with shorter names parses cleanly.
23334    #[test]
23335    #[hardwon(
23336        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23337    )]
23338    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23339        let source = r#"    LIBRARY_DEPRECATED(
23340        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23341        "use library_init_from_file_with_params instead"
23342    );
23343    LIBRARY_DEPRECATED(
23344        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23345        "use library_init_from_buffer_with_params instead"
23346    );
23347    LIBRARY_DEPRECATED(
23348        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23349        "use library_init_with_params instead"
23350    );
23351"#;
23352        let mut parser = tree_sitter::Parser::new();
23353        parser
23354            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23355            .expect("C++ grammar");
23356        let tree = parser.parse(source, None).expect("C++ tree");
23357        let root = tree.root_node();
23358
23359        // The shape this recovery exists for: three sibling `ERROR` envelopes,
23360        // each holding one whole invocation, each followed by the statement
23361        // that owns the invocation's `;`.
23362        let mut cursor = root.walk();
23363        let items = root.children(&mut cursor).collect::<Vec<_>>();
23364        let [
23365            first,
23366            first_statement,
23367            second,
23368            second_statement,
23369            third,
23370            third_statement,
23371        ] = items.as_slice()
23372        else {
23373            panic!("{}", root.to_sexp());
23374        };
23375        let envelopes = [
23376            (first, first_statement),
23377            (second, second_statement),
23378            (third, third_statement),
23379        ];
23380        for (envelope, statement) in envelopes {
23381            assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23382            assert_eq!(
23383                statement.kind(),
23384                "expression_statement",
23385                "{}",
23386                root.to_sexp()
23387            );
23388            assert!(
23389                node_text(*envelope, source).ends_with(')'),
23390                "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23391                root.to_sexp()
23392            );
23393        }
23394
23395        // Each is admitted, and each names the byte just past its own `;`. A
23396        // self-contained invocation swallows nothing, so the recovery owns
23397        // only its own bytes.
23398        let ancestry = ParentIndex::new(root);
23399        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23400            .expect("the first invocation");
23401        assert_eq!(
23402            &source[..first_run.invocation_end],
23403            &source[..source.find("instead\"\n    );").expect("first hint")
23404                + "instead\"\n    );".len()]
23405        );
23406        for (envelope, _) in envelopes {
23407            let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23408                .expect("each invocation");
23409            assert_eq!(
23410                run.region_end, run.invocation_end,
23411                "a self-contained invocation owns only its own bytes"
23412            );
23413        }
23414
23415        let parsed = parse_cpp_declarations(source, "library.h");
23416        assert_eq!(
23417            function_identities(&parsed),
23418            vec![
23419                (
23420                    "library_init".to_string(),
23421                    "(struct library_model_loader *)".to_string()
23422                ),
23423                (
23424                    "library_init_from_buffer".to_string(),
23425                    "(void *, size_t)".to_string()
23426                ),
23427                (
23428                    "library_init_from_file".to_string(),
23429                    "(const char *)".to_string()
23430                ),
23431            ],
23432            "{:#?}",
23433            parsed.declarations()
23434        );
23435    }
23436
23437    #[test]
23438    #[hardwon(
23439        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23440    )]
23441    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23442        let source = r#"void host() {
23443#ifdef ENABLED
23444LIBRARY_DEPRECATED(
23445    int hidden(const char* value),
23446    "hint"
23447);
23448#endif
23449}
23450"#;
23451        let mut parser = tree_sitter::Parser::new();
23452        parser
23453            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23454            .expect("C++ grammar");
23455        let tree = parser.parse(source, None).expect("C++ tree");
23456        let root = tree.root_node();
23457        let function = root.named_child(0).expect("ordinary function");
23458        let body = function
23459            .child_by_field_name("body")
23460            .expect("ordinary function body");
23461        let conditional = body.named_child(0).expect("conditional body");
23462        let mut conditional_cursor = conditional.walk();
23463        let envelope = conditional
23464            .named_children(&mut conditional_cursor)
23465            .find(|child| child.kind() == "ERROR")
23466            .expect("macro error");
23467        assert_eq!(conditional.kind(), "preproc_ifdef");
23468        assert_eq!(envelope.kind(), "ERROR");
23469
23470        let ancestry = ParentIndex::new(root);
23471        assert!(declaration_scope_container(envelope, &ancestry).is_none());
23472        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23473
23474        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23475        assert!(
23476            parsed
23477                .declarations()
23478                .iter()
23479                .all(|unit| unit.fq_name() != "hidden"),
23480            "a function-body macro error must not become a global declaration: {:#?}",
23481            parsed.declarations()
23482        );
23483    }
23484
23485    /// The hand-off that keeps the scan finite. An invocation that reaches the
23486    /// end of its own declaration scope swallowed nothing, and the ordinary
23487    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
23488    /// that region for every invocation it finds, so admitting it here would
23489    /// hand the scan its own input back.
23490    #[test]
23491    #[hardwon(
23492        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23493    )]
23494    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23495        let source = r#"LIBRARY_DEPRECATED(
23496        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23497        "use library_init_from_file_with_params instead"
23498    );"#;
23499        let mut parser = tree_sitter::Parser::new();
23500        parser
23501            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23502            .expect("C++ grammar");
23503        let tree = parser.parse(source, None).expect("C++ tree");
23504        let root = tree.root_node();
23505        let head = root.named_child(0).expect("the invocation");
23506        let ancestry = ParentIndex::new(root);
23507        assert!(
23508            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23509            "{}",
23510            root.to_sexp()
23511        );
23512        assert!(
23513            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23514            "the ordinary reader must be the one that has it: {}",
23515            root.to_sexp()
23516        );
23517        // The index is a speed substitution, never a semantic one: the
23518        // unindexed path is `Node::parent` itself and must agree.
23519        let unindexed = ParentIndex::unindexed();
23520        assert!(
23521            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23522            "indexed and unindexed climbs must agree"
23523        );
23524        assert_eq!(
23525            macro_wrapped_declarations(head, source, &ancestry).len(),
23526            macro_wrapped_declarations(head, source, &unindexed).len(),
23527            "indexed and unindexed climbs must agree"
23528        );
23529    }
23530
23531    /// Negative controls for the same recovery: a macro invocation whose
23532    /// arguments are not a declaration recovers nothing, whether the parser
23533    /// keeps it clean, reads the arguments as a type, or reads them as a bare
23534    /// declarator.
23535    #[test]
23536    #[hardwon(
23537        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23538    )]
23539    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23540        for source in [
23541            "int before;\nFOO(1, 2);\nint after;\n",
23542            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23543            "int before;\nMACRO(int a, int b);\nint after;\n",
23544            "DECLARE_HANDLE(HWND);\nint after;\n",
23545        ] {
23546            let parsed = parse_cpp_declarations(source, "macro-call.h");
23547            assert_eq!(
23548                function_identities(&parsed),
23549                Vec::new(),
23550                "{source:?} must declare no function: {:#?}",
23551                parsed.declarations()
23552            );
23553        }
23554    }
23555
23556    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
23557    /// makes tree-sitter read the macro as the member's type and its argument
23558    /// list as a parenthesized declarator, which then swallows the attributed
23559    /// member and the member written after it. Both are members.
23560    #[test]
23561    #[hardwon(
23562        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23563    )]
23564    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23565        let source = r#"#include <string_view>
23566namespace Botan {
23567class DL_Group final {
23568   public:
23569      DL_Group() = default;
23570      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23571      DL_Group(std::string_view pem, int format);
23572      size_t get_p() const;
23573};
23574}
23575"#;
23576        let parsed = parse_cpp_declarations(source, "dl_group.h");
23577        assert_eq!(
23578            function_identities(&parsed),
23579            vec![
23580                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23581                (
23582                    "Botan.DL_Group.DL_Group".to_string(),
23583                    "(std::string_view)".to_string()
23584                ),
23585                (
23586                    "Botan.DL_Group.DL_Group".to_string(),
23587                    "(std::string_view, int)".to_string()
23588                ),
23589                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23590            ],
23591            "{:#?}",
23592            parsed.declarations()
23593        );
23594    }
23595
23596    /// #2552 shape 3, export-macro class. The class head macro makes the body a
23597    /// `compound_statement`, so members come from the region reparse, and one
23598    /// string-attribute member used to make that reparse unindexable -- which
23599    /// left the class with no members at all.
23600    #[test]
23601    #[hardwon(
23602        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23603    )]
23604    fn an_export_macro_class_keeps_its_string_attribute_members() {
23605        let source = r#"#include <string_view>
23606namespace Botan {
23607class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23608   public:
23609      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23610      DL_Group(std::string_view pem, int format);
23611      size_t get_p() const;
23612};
23613}
23614"#;
23615        let parsed = parse_cpp_declarations(source, "dl_group.h");
23616        assert!(
23617            parsed
23618                .declarations()
23619                .iter()
23620                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23621            "{:#?}",
23622            parsed.declarations()
23623        );
23624        assert_eq!(
23625            function_identities(&parsed),
23626            vec![
23627                (
23628                    "Botan.DL_Group.DL_Group".to_string(),
23629                    "(std::string_view)".to_string()
23630                ),
23631                (
23632                    "Botan.DL_Group.DL_Group".to_string(),
23633                    "(std::string_view, int)".to_string()
23634                ),
23635                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23636            ],
23637            "{:#?}",
23638            parsed.declarations()
23639        );
23640    }
23641
23642    /// #2552 shape 3, the `= default` variant plus the access-label
23643    /// constructor. The attributed defaulted constructor separates cleanly, but
23644    /// it strands the next member in a bare `ERROR`, and a constructor written
23645    /// with a member-initializer list under `private:` dissolves into
23646    /// expression soup that the reparse recovers from its own source range.
23647    #[test]
23648    #[hardwon(
23649        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23650    )]
23651    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23652        let source = r#"namespace Botan {
23653class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23654   public:
23655      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23656      XMSS_Parameters(int oid, int len);
23657      size_t len() const;
23658
23659   private:
23660      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23661            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
23662
23663      int m_oid;
23664      int m_wots_oid;
23665      size_t m_element_size;
23666      size_t m_tree_height;
23667};
23668}
23669"#;
23670        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
23671        let constructors = function_identities(&parsed)
23672            .into_iter()
23673            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
23674            .map(|(_, signature)| signature)
23675            .collect::<Vec<_>>();
23676        assert_eq!(
23677            constructors,
23678            vec![
23679                "()".to_string(),
23680                "(int, int)".to_string(),
23681                "(int, int, size_t, size_t)".to_string(),
23682            ],
23683            "{:#?}",
23684            parsed.declarations()
23685        );
23686    }
23687
23688    /// Negative control for the same rule: a real qualified name spells its
23689    /// `::` in the source, so the separator is present rather than MISSING and
23690    /// the out-of-line definition keeps its owner.
23691    #[test]
23692    #[hardwon(
23693        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
23694    )]
23695    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
23696        let source = r#"namespace shell {
23697struct Outer {
23698   struct Inner {
23699      Inner(int v);
23700      void run(int v);
23701   };
23702};
23703Outer::Inner::Inner(int v) {}
23704void Outer::Inner::run(int v) {}
23705}
23706"#;
23707        let parsed = parse_cpp_declarations(source, "outer.cpp");
23708        let names = function_identities(&parsed)
23709            .into_iter()
23710            .map(|(fq_name, _)| fq_name)
23711            .collect::<Vec<_>>();
23712        assert!(
23713            names
23714                .iter()
23715                .all(|name| name.starts_with("shell.Outer$Inner.")),
23716            "{names:#?}"
23717        );
23718    }
23719
23720    #[test]
23721    #[hardwon(
23722        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
23723    )]
23724    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
23725        let source = r#"namespace control {
23726template <typename T>
23727class AnySpan;
23728template <typename T>
23729class ABSL_ATTRIBUTE_VIEW AnySpan {
23730 public:
23731  int begin() const;
23732};
23733}
23734
23735namespace absl {
23736ABSL_NAMESPACE_BEGIN
23737template <typename T>
23738class ABSL_ATTRIBUTE_VIEW Span {
23739 public:
23740  int begin() const;
23741  int back() const;
23742};
23743
23744int begin();
23745int back();
23746}
23747"#;
23748        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
23749        let declarations = parsed.declarations();
23750        assert!(
23751            declarations
23752                .iter()
23753                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
23754        );
23755        for method in ["begin", "back"] {
23756            assert!(declarations.iter().any(|unit| {
23757                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
23758            }));
23759            assert!(
23760                declarations.iter().any(|unit| {
23761                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
23762                })
23763            );
23764        }
23765        assert!(
23766            declarations
23767                .iter()
23768                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
23769        );
23770        assert!(
23771            declarations
23772                .iter()
23773                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
23774        );
23775        assert!(
23776            declarations
23777                .iter()
23778                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
23779        );
23780    }
23781
23782    #[test]
23783    #[hardwon(
23784        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23785    )]
23786    fn explicit_global_member_definition_has_canonical_package_boundary() {
23787        let source = r#"
23788namespace arangodb::aql {
23789class ExecutionPlan {
23790 public:
23791  template<class... Args> Node* createNode(Args&&... args);
23792};
23793}
23794
23795template<class... Args>
23796Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
23797"#;
23798        let parsed = parse_cpp_declarations(source, "global-member.cpp");
23799
23800        assert!(parsed.declarations().iter().any(|unit| {
23801            unit.is_function()
23802                && unit.package_name() == "arangodb::aql"
23803                && unit.short_name() == "ExecutionPlan.createNode"
23804                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
23805        }));
23806    }
23807
23808    #[test]
23809    #[hardwon(
23810        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23811    )]
23812    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
23813        let source = r#"
23814#ifndef TINYXML2_INCLUDED
23815#define TINYXML2_INCLUDED
23816namespace tinyxml2 {
23817class TINYXML2_LIB XMLUtil {
23818 public:
23819  static const char* SkipWhiteSpace(const char* p) {
23820    while (*p) {
23821      if (*p == ' ') {
23822        ++p;
23823      }
23824    }
23825    return p;
23826  }
23827  static bool StringEqual(const char* p, const char* q) {
23828    return p == q;
23829  }
23830  class TINYXML2_LIB Helper {
23831   public:
23832    void Touch();
23833  };
23834  static void ToStr(int value, char* buffer);
23835 private:
23836  static const char* writeBoolTrue;
23837};
23838
23839class TINYXML2_LIB XMLNode {
23840 public:
23841  virtual XMLNode* ShallowClone() const = 0;
23842  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
23843};
23844}
23845#endif
23846"#;
23847        let mut parser = tree_sitter::Parser::new();
23848        parser
23849            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23850            .unwrap();
23851        let tree = parser.parse(source, None).unwrap();
23852        let mut boundary_found = false;
23853        walk_named_tree_preorder(tree.root_node(), true, |node| {
23854            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
23855                && name == "XMLUtil"
23856            {
23857                boundary_found = fragmented_export_sibling_class_boundary(
23858                    node,
23859                    source,
23860                    &ParentIndex::unindexed(),
23861                )
23862                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
23863                .is_some_and(|(_, name, _, _)| name == "XMLNode");
23864            }
23865            WalkControl::Continue
23866        });
23867        assert!(
23868            boundary_found,
23869            "fixture must exercise the recovered sibling boundary"
23870        );
23871
23872        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
23873        assert!(
23874            parsed
23875                .declarations()
23876                .iter()
23877                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
23878            "{:#?}",
23879            parsed.declarations()
23880        );
23881        assert!(
23882            parsed
23883                .declarations()
23884                .iter()
23885                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
23886            "{:#?}",
23887            parsed.declarations()
23888        );
23889        assert!(parsed.declarations().iter().any(|unit| {
23890            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
23891        }));
23892        assert!(
23893            parsed
23894                .declarations()
23895                .iter()
23896                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
23897        );
23898        assert!(
23899            parsed
23900                .declarations()
23901                .iter()
23902                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
23903        );
23904    }
23905
23906    #[test]
23907    #[hardwon(
23908        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23909    )]
23910    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
23911        // Clang's diagnostic suite intentionally contains this ill-formed
23912        // spelling. The analyzer must retain the parser's explicit-global AST
23913        // boundary instead of constructing `cwg311::::cwg311::X`.
23914        let parsed = parse_cpp_declarations(
23915            r#"
23916namespace cwg311 {
23917namespace X { namespace Y {} }
23918namespace ::cwg311::X {}
23919}
23920"#,
23921            "explicit-global-namespace.cpp",
23922        );
23923
23924        assert!(parsed.declarations().iter().any(|unit| {
23925            unit.kind() == CodeUnitType::Module
23926                && unit.short_name() == "cwg311::X"
23927                && unit.fq_name() == "cwg311::X"
23928        }));
23929        assert!(
23930            parsed
23931                .declarations()
23932                .iter()
23933                .all(|unit| !unit.short_name().contains("::::")),
23934            "recovered namespace names must not retain empty scope components: {:#?}",
23935            parsed.declarations()
23936        );
23937    }
23938
23939    #[test]
23940    #[hardwon(
23941        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23942    )]
23943    fn repeated_scope_separator_does_not_create_empty_function_owner() {
23944        let scope = ScopeInfo {
23945            package_name: "X".to_string(),
23946            module: None,
23947            class_unit: None,
23948            template_signature: None,
23949            template_metadata: None,
23950            declarations_are_fields: false,
23951            recovered_specialization_member_scope: false,
23952            visible_using_namespaces: Vec::new(),
23953            visible_using_declarations: Arc::default(),
23954        };
23955
23956        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
23957
23958        assert!(owner.is_none());
23959        assert_eq!(name, "doit");
23960        assert_eq!(package, "X");
23961    }
23962
23963    #[test]
23964    #[hardwon(
23965        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23966    )]
23967    fn trailing_decltype_expression_is_not_a_function_declarator() {
23968        let source = r#"
23969namespace boost { namespace detail {
23970#if ! defined(BOOST_NO_SFINAE_EXPR) && \
23971    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
23972    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
23973#define BOOST_THREAD_PROVIDES_INVOKE
23974#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
23975template <class Fp, class A0, class ...Args>
23976inline auto
23977invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
23978       BOOST_THREAD_RV_REF(Args) ...args)
23979    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
23980{
23981    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
23982}
23983#endif
23984#endif
23985}}
23986"#;
23987        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
23988
23989        assert!(
23990            parsed
23991                .declarations()
23992                .iter()
23993                .all(|unit| unit.short_name() != ".*f")
23994        );
23995    }
23996
23997    fn find_class_named<'tree>(
23998        root: Node<'tree>,
23999        source: &str,
24000        expected_name: &str,
24001    ) -> Option<Node<'tree>> {
24002        let mut stack = vec![root];
24003        while let Some(node) = stack.pop() {
24004            if node.kind() == "class_specifier"
24005                && node
24006                    .child_by_field_name("name")
24007                    .is_some_and(|name| node_text(name, source) == expected_name)
24008            {
24009                return Some(node);
24010            }
24011            let mut cursor = node.walk();
24012            stack.extend(node.named_children(&mut cursor));
24013        }
24014        None
24015    }
24016
24017    #[test]
24018    #[hardwon(
24019        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24020    )]
24021    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24022        let source = r#"EXPORT void definition(struct Value value) {}
24023EXPORT void prototype(struct Value value);
24024"#;
24025        let mut parser = tree_sitter::Parser::new();
24026        parser
24027            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24028            .unwrap();
24029        let tree = parser.parse(source, None).unwrap();
24030        let root = tree.root_node();
24031        let mut cursor = root.walk();
24032        let callables = root
24033            .named_children(&mut cursor)
24034            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24035            .collect::<Vec<_>>();
24036
24037        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24038        for callable in callables {
24039            // The macro-declaration grammar parses these cleanly, with the
24040            // export macro retained as a `macro_modifier`; the preserved
24041            // callable and its `struct` parameter are what the sentinel gate
24042            // rejects on, with or without an ERROR in the envelope.
24043            assert!(
24044                cpp_sentinel_macro_parts(callable, source).is_none(),
24045                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24046            );
24047        }
24048    }
24049
24050    #[test]
24051    #[hardwon(
24052        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24053    )]
24054    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24055        let source = r#"namespace absl {
24056ABSL_NAMESPACE_BEGIN
24057// Generate a floating-point variate conforming to a Beta distribution:
24058template <typename RealType = double>
24059class beta_distribution {
24060 public:
24061  using result_type = RealType;
24062
24063
24064  beta_distribution() : beta_distribution(1) {}
24065
24066  explicit beta_distribution(result_type alpha, result_type beta = 1)
24067      : param_(alpha, beta) {}
24068
24069  explicit beta_distribution(const param_type& p) : param_(p) {}
24070
24071  void reset() {}
24072
24073  // Generating functions
24074  template <typename URBG>
24075  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
24076    return (*this)(g, param_);
24077  }
24078
24079};
24080ABSL_NAMESPACE_END
24081}  // namespace absl
24082"#;
24083        let mut parser = tree_sitter::Parser::new();
24084        parser
24085            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24086            .unwrap();
24087        let tree = parser.parse(source, None).unwrap();
24088        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24089        let body = namespace
24090            .child_by_field_name("body")
24091            .expect("fixture namespace body");
24092        let sentinel = body.named_child(0).expect("sentinel envelope");
24093
24094        // The macro-declaration grammar keeps the sentinel head in an `ERROR`
24095        // envelope that ends at the class name, with the class body as the
24096        // sibling it recovered with; the bogus callable the older grammar
24097        // assembled from the last member is no longer part of the envelope.
24098        assert_eq!(sentinel.kind(), "ERROR");
24099        assert!(
24100            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24101            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24102        );
24103
24104        let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24105        for expected in [
24106            "absl.beta_distribution",
24107            "absl.beta_distribution.beta_distribution",
24108            "absl.beta_distribution.reset",
24109        ] {
24110            assert!(
24111                parsed
24112                    .declarations()
24113                    .iter()
24114                    .any(|unit| unit.fq_name() == expected),
24115                "missing recovered declaration {expected}: {:#?}",
24116                parsed.declarations()
24117            );
24118        }
24119    }
24120
24121    #[test]
24122    #[hardwon(
24123        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24124    )]
24125    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24126        let source = r#"namespace absl {
24127ABSL_NAMESPACE_BEGIN
24128// absl::discrete_distribution
24129//
24130// A discrete distribution produces random integers i, where 0 <= i < n
24131template <typename IntType = int>
24132class discrete_distribution {
24133 public:
24134  using result_type = IntType;
24135  class param_type {
24136   public:
24137    param_type() { init(); }
24138    template <typename InputIterator>
24139    explicit param_type(InputIterator begin, InputIterator end)
24140        : p_(begin, end) {
24141      init();
24142    }
24143  };
24144  discrete_distribution() : param_() {}
24145  explicit discrete_distribution(const param_type& p) : param_(p) {}
24146};
24147ABSL_NAMESPACE_END
24148}  // namespace absl
24149"#;
24150        let mut parser = tree_sitter::Parser::new();
24151        parser
24152            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24153            .unwrap();
24154        let tree = parser.parse(source, None).unwrap();
24155        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24156        let body = namespace
24157            .child_by_field_name("body")
24158            .expect("fixture namespace body");
24159        let sentinel = body.named_child(0).expect("sentinel envelope");
24160        let callable = sentinel
24161            .child_by_field_name("declarator")
24162            .and_then(extract_function_declarator)
24163            .and_then(cpp_function_declarator_name_node)
24164            .expect("preserved callable name");
24165
24166        assert_eq!(sentinel.kind(), "function_definition");
24167        assert_eq!(callable.kind(), "identifier");
24168        assert!(
24169            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24170            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24171        );
24172    }
24173
24174    #[test]
24175    #[hardwon(
24176        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24177    )]
24178    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24179        let source = r#"
24180#define CPPCHECKLIB
24181class Library {
24182    struct Container {
24183        CPPCHECKLIB static std::string toString(Yield yield);
24184        CPPCHECKLIB static std::string toString(Action action);
24185    };
24186};
24187"#;
24188        let mut parser = tree_sitter::Parser::new();
24189        parser
24190            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24191            .unwrap();
24192        let tree = parser.parse(source, None).unwrap();
24193        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24194        let parsed = parse_cpp_file(&file, source, &tree);
24195        assert!(
24196            parsed
24197                .declarations()
24198                .iter()
24199                .all(|unit| unit.fq_name() != "Library$Container.std"),
24200            "the qualified return-type namespace must not become a field: {:#?}",
24201            parsed.declarations()
24202        );
24203        for expected in ["(Yield)", "(Action)"] {
24204            assert!(
24205                parsed.declarations().iter().any(|unit| {
24206                    unit.is_function()
24207                        && unit.fq_name() == "Library$Container.toString"
24208                        && unit.signature() == Some(expected)
24209                }),
24210                "recovered toString overload {expected} is missing: {:#?}",
24211                parsed.declarations()
24212            );
24213        }
24214    }
24215
24216    #[test]
24217    #[hardwon(
24218        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24219    )]
24220    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24221        let source = r#"
24222#define SIMPLECPP_LIB
24223namespace simplecpp {
24224using TokenString = std::string;
24225struct Location { int line{}; };
24226class SIMPLECPP_LIB Token {
24227  TokenString prefix;
24228  void prefix_method() {}
24229 public:
24230  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24231      whitespaceahead(wsahead), location(loc), string(s)
24232      // The comment must not hide the constructor body from recovery.
24233      {
24234      flags();
24235  }
24236  TokenString string;
24237  bool whitespaceahead;
24238  Location location;
24239  Token *previous{};
24240 private:
24241  void flags() {
24242      whitespaceahead = true;
24243  }
24244};
24245}
24246"#;
24247        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24248
24249        let location_fields = parsed
24250            .declarations()
24251            .iter()
24252            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24253            .collect::<Vec<_>>();
24254        assert_eq!(
24255            location_fields.len(),
24256            1,
24257            "location should have one class-owned declaration: {:#?}",
24258            parsed.declarations()
24259        );
24260        assert!(
24261            location_fields[0].is_field(),
24262            "location has wrong kind: {:#?}",
24263            parsed.declarations()
24264        );
24265        assert!(
24266            parsed.declarations().iter().all(|unit| {
24267                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24268            })
24269        );
24270        assert!(
24271            parsed.declarations().iter().all(|unit| {
24272                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24273            })
24274        );
24275        assert!(
24276            parsed
24277                .declarations()
24278                .iter()
24279                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24280        );
24281        assert!(
24282            parsed
24283                .declarations()
24284                .iter()
24285                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24286            "the recovered class must retain its constructor: {:#?}",
24287            parsed.declarations()
24288        );
24289        assert!(
24290            parsed
24291                .declarations()
24292                .iter()
24293                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24294        );
24295        assert!(parsed.declarations().iter().any(|unit| {
24296            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24297        }));
24298        let constructor = parsed
24299            .declarations()
24300            .iter()
24301            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24302            .expect("recovered constructor");
24303        let constructor_start = source.find("Token(const").expect("constructor start");
24304        let constructor_end = source
24305            .get(
24306                ..source
24307                    .find("  TokenString string;")
24308                    .expect("constructor end"),
24309            )
24310            .expect("constructor slice")
24311            .trim_end()
24312            .len();
24313        assert!(
24314            parsed
24315                .navigation_ranges
24316                .get(constructor)
24317                .is_some_and(|ranges| {
24318                    ranges.iter().any(|range| {
24319                        range.start_byte == constructor_start && range.end_byte == constructor_end
24320                    })
24321                }),
24322            "constructor navigation must span the full body: {:#?}",
24323            parsed.navigation_ranges
24324        );
24325        assert_eq!(
24326            parsed
24327                .signature_metadata
24328                .get(constructor)
24329                .and_then(|metadata| metadata.first())
24330                .and_then(SignatureMetadata::callable_linkage),
24331            Some(CallableLinkage::External)
24332        );
24333        let token_class = parsed
24334            .declarations()
24335            .iter()
24336            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24337            .expect("recovered Token class");
24338        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24339        assert!(
24340            parsed
24341                .navigation_ranges
24342                .get(token_class)
24343                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24344            "class navigation must include the terminating semicolon: {:#?}",
24345            parsed.navigation_ranges
24346        );
24347    }
24348
24349    #[test]
24350    #[hardwon(
24351        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24352    )]
24353    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24354        let source = r#"
24355#define SIMPLECPP_LIB
24356namespace simplecpp {
24357using TokenString = std::string;
24358class Macro;
24359struct Location {
24360  unsigned int fileIndex{};
24361  unsigned int line{};
24362  unsigned int col{};
24363};
24364struct Output {
24365  int type;
24366};
24367class SIMPLECPP_LIB Token {
24368 public:
24369  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24370      whitespaceahead(wsahead), location(loc), string(s) {
24371      flags();
24372  }
24373  Token(const Token &tok) :
24374      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24375      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24376      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24377  Token &operator=(const Token &tok) = delete;
24378  const TokenString& str() const { return string; }
24379  void setstr(const std::string &s) { string = s; flags(); }
24380  bool isOneOf(const char ops[]) const;
24381  TokenString macro;
24382  char op;
24383  bool comment;
24384  bool name;
24385  bool number;
24386  bool whitespaceahead;
24387  Location location;
24388  Token *previous{};
24389  Token *next{};
24390 private:
24391  void flags() {
24392      name = !string.empty();
24393      comment = false;
24394      number = false;
24395      op = 0;
24396  }
24397  TokenString string;
24398};
24399}
24400struct Following {
24401  int type;
24402};
24403class SIMPLECPP_LIB Later {
24404 public:
24405  Later(int value) : value(value) {}
24406  int value;
24407};
24408"#;
24409        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24410        assert!(
24411            parsed
24412                .declarations()
24413                .iter()
24414                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24415        );
24416        assert!(
24417            !parsed
24418                .declarations()
24419                .iter()
24420                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24421        );
24422        assert!(
24423            parsed
24424                .declarations()
24425                .iter()
24426                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24427        );
24428        assert!(
24429            !parsed
24430                .declarations()
24431                .iter()
24432                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24433        );
24434        assert!(
24435            parsed
24436                .declarations()
24437                .iter()
24438                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24439        );
24440        assert!(
24441            parsed
24442                .declarations()
24443                .iter()
24444                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24445        );
24446        assert!(
24447            parsed
24448                .declarations()
24449                .iter()
24450                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24451        );
24452        assert!(
24453            parsed
24454                .declarations()
24455                .iter()
24456                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24457        );
24458        assert!(
24459            parsed
24460                .declarations()
24461                .iter()
24462                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24463        );
24464        assert!(
24465            parsed
24466                .declarations()
24467                .iter()
24468                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24469        );
24470        assert!(parsed.declarations().iter().all(|unit| {
24471            !matches!(
24472                unit.fq_name().as_str(),
24473                "simplecpp.Token.Following" | "simplecpp.Token.Later"
24474            )
24475        }));
24476        assert!(
24477            !parsed
24478                .declarations()
24479                .iter()
24480                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24481            "the following struct must remain outside the recovered Token class"
24482        );
24483    }
24484
24485    #[test]
24486    #[hardwon(
24487        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24488    )]
24489    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24490        let source = r#"
24491#define SIMPLECPP_LIB
24492namespace {
24493namespace simplecpp {
24494using TokenString = std::string;
24495struct Location { int line{}; };
24496class SIMPLECPP_LIB HiddenToken {
24497 public:
24498  HiddenToken(const TokenString &s, const Location &loc) :
24499      location(loc), string(s) {
24500      flags();
24501  }
24502  TokenString string;
24503  Location location;
24504  HiddenToken *previous{};
24505 private:
24506  void flags() {}
24507};
24508}
24509}
24510"#;
24511        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24512        let constructor = parsed
24513            .declarations()
24514            .iter()
24515            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24516            .expect("recovered anonymous-namespace constructor");
24517        assert_eq!(
24518            parsed
24519                .signature_metadata
24520                .get(constructor)
24521                .and_then(|metadata| metadata.first())
24522                .and_then(SignatureMetadata::callable_linkage),
24523            Some(CallableLinkage::Internal)
24524        );
24525    }
24526
24527    #[test]
24528    #[hardwon(
24529        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24530    )]
24531    fn macro_qualified_static_field_keeps_real_declarator() {
24532        let source = r#"#define JSON_INLINE_VARIABLE
24533struct Reader {
24534static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24535static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24536static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24537};"#;
24538        let mut parser = tree_sitter::Parser::new();
24539        parser
24540            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24541            .unwrap();
24542        let tree = parser.parse(source, None).unwrap();
24543        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24544        let parsed = parse_cpp_file(&file, source, &tree);
24545        for expected in [
24546            "Reader.npos",
24547            "Reader.other",
24548            "Reader.pointer",
24549            "Reader.reference",
24550        ] {
24551            assert!(
24552                parsed
24553                    .declarations()
24554                    .iter()
24555                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
24556                "real macro-decorated field {expected} is missing: {:#?}",
24557                parsed.declarations()
24558            );
24559        }
24560        assert!(
24561            parsed
24562                .declarations()
24563                .iter()
24564                .all(|unit| unit.fq_name() != "Reader.std"),
24565            "qualified type prefix became a pseudo-field: {:#?}",
24566            parsed.declarations()
24567        );
24568        let root = tree.root_node();
24569        let mut stack = vec![root];
24570        let mut signatures = Vec::new();
24571        // The macro-declaration grammar keeps `JSON_INLINE_VARIABLE` as a
24572        // `macro_modifier`, so the fields arrive as ordinary clean field
24573        // declarations whose declarators carry the signature text the
24574        // bitfield-clause recovery used to reconstruct.
24575        while let Some(current) = stack.pop() {
24576            if current.kind() == "field_declaration" {
24577                let mut cursor = current.walk();
24578                for declarator in current.children_by_field_name("declarator", &mut cursor) {
24579                    signatures.push(render_cpp_field_signature(current, declarator, source));
24580                }
24581            }
24582            let mut cursor = current.walk();
24583            stack.extend(current.named_children(&mut cursor));
24584        }
24585        signatures.sort();
24586        assert_eq!(
24587            signatures,
24588            [
24589                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24590                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24591                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24592                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24593            ]
24594        );
24595    }
24596
24597    fn member_function_linkage(source: &str) -> CallableLinkage {
24598        let mut parser = tree_sitter::Parser::new();
24599        parser
24600            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24601            .unwrap();
24602        let tree = parser.parse(source, None).unwrap();
24603        let ancestry = ParentIndex::new(tree.root_node());
24604        let mut stack = vec![tree.root_node()];
24605        while let Some(node) = stack.pop() {
24606            if node.kind() == "function_definition" {
24607                let mut current = node.parent();
24608                while let Some(parent) = current {
24609                    if matches!(
24610                        parent.kind(),
24611                        "class_specifier" | "struct_specifier" | "union_specifier"
24612                    ) {
24613                        return cpp_callable_linkage(node, source, &ancestry);
24614                    }
24615                    current = parent.parent();
24616                }
24617            }
24618            let mut cursor = node.walk();
24619            stack.extend(node.named_children(&mut cursor));
24620        }
24621        panic!("fixture has no member function definition");
24622    }
24623
24624    #[test]
24625    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24626        assert_eq!(
24627            member_function_linkage("struct Named { int method() { return 1; } };"),
24628            CallableLinkage::External
24629        );
24630        assert_eq!(
24631            member_function_linkage(
24632                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24633            ),
24634            CallableLinkage::Internal
24635        );
24636        assert_eq!(
24637            member_function_linkage("struct { int method() { return 1; } } instance;"),
24638            CallableLinkage::Internal
24639        );
24640        assert_eq!(
24641            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24642            CallableLinkage::Internal
24643        );
24644    }
24645
24646    #[test]
24647    #[hardwon(
24648        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24649    )]
24650    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24651        let source = r#"
24652#ifndef PROTON_VALUE_HPP
24653#define PROTON_VALUE_HPP
24654namespace proton {
24655namespace internal {
24656class value_base {
24657  protected:
24658    internal::data& data();
24659    internal::data data_;
24660  friend class codec::encoder;
24661  friend class codec::decoder;
24662};
24663}
24664class value : public internal::value_base, private internal::comparable<value> {
24665  private:
24666    template<class T, class U=void> struct assignable :
24667        public std::enable_if<codec::is_encodable<T>::value, U> {};
24668    template<class U> struct assignable<value, U> {};
24669  public:
24670    PN_CPP_EXTERN value();
24671    PN_CPP_EXTERN value(const value&);
24672    PN_CPP_EXTERN value& operator=(const value&);
24673    PN_CPP_EXTERN value(value&&);
24674    PN_CPP_EXTERN value& operator=(value&&);
24675    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
24676    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
24677        codec::encoder e(*this);
24678        e << x;
24679        return *this;
24680    }
24681    PN_CPP_EXTERN type_id type() const;
24682    PN_CPP_EXTERN bool empty() const;
24683    PN_CPP_EXTERN void clear();
24684    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
24685    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
24686  friend PN_CPP_EXTERN void swap(value&, value&);
24687  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
24688  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
24689  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
24690    value(pn_data_t* d);
24691    void reset(pn_data_t* d = 0);
24692};
24693}
24694#endif
24695"#;
24696        let mut parser = tree_sitter::Parser::new();
24697        parser
24698            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24699            .unwrap();
24700        let tree = parser.parse(source, None).unwrap();
24701        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
24702        let parsed = parse_cpp_file(&file, source, &tree);
24703        // The macro-declaration grammar parses the class and its decorated
24704        // members cleanly, so the constructors mint with their true
24705        // class-owned identity (`proton.value.value`) rather than the
24706        // namespace-scope identity the fragment recovery produced before.
24707        let macro_constructors = parsed
24708            .signature_metadata
24709            .iter()
24710            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
24711            .flat_map(|(_, metadata)| metadata)
24712            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
24713            .collect::<Vec<_>>();
24714
24715        assert_eq!(
24716            macro_constructors.len(),
24717            3,
24718            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
24719            parsed.declarations()
24720        );
24721        assert!(
24722            macro_constructors.iter().all(|metadata| {
24723                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
24724            }),
24725            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
24726        );
24727    }
24728
24729    #[test]
24730    #[hardwon(
24731        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
24732    )]
24733    fn recovered_export_class_typedef_uses_displaced_alias_name() {
24734        let source = r#"
24735namespace spi {
24736class Filter {
24737public:
24738    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
24739};
24740}
24741namespace filter {
24742class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
24743{
24744public:
24745    typedef spi::Filter BASE_CLASS;
24746    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
24747    BEGIN_LOG4CXX_CAST_MAP()
24748    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
24749    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
24750    END_LOG4CXX_CAST_MAP()
24751    FilterDecision decide() const;
24752};
24753}
24754"#;
24755        let mut parser = tree_sitter::Parser::new();
24756        parser
24757            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24758            .unwrap();
24759        let tree = parser.parse(source, None).unwrap();
24760        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
24761        let parsed = parse_cpp_file(&file, source, &tree);
24762        assert!(
24763            parsed.declarations().iter().any(|unit| {
24764                unit.is_class()
24765                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
24766                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
24767            }),
24768            "the displaced typedef alias must retain its declared name: {:#?}",
24769            parsed.declarations()
24770        );
24771        assert!(
24772            parsed
24773                .declarations()
24774                .iter()
24775                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
24776            "the qualified underlying type must not become a false nested alias: {:#?}",
24777            parsed.declarations()
24778        );
24779    }
24780
24781    #[test]
24782    #[hardwon(
24783        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24784    )]
24785    fn exported_single_base_recovery_uses_displaced_class_name() {
24786        let source = r#"
24787class CORE_EXPORT QgsPoint : public AbstractGeometry
24788{
24789    Q_GADGET
24790
24791    Q_PROPERTY( double x READ x WRITE setX )
24792    Q_PROPERTY( double y READ y WRITE setY )
24793    Q_PROPERTY( double z READ z WRITE setZ )
24794    Q_PROPERTY( double m READ m WRITE setM )
24795
24796  public:
24797#ifndef SIP_RUN
24798    QgsPoint(
24799      double x = std::numeric_limits<double>::quiet_NaN(),
24800      double y = std::numeric_limits<double>::quiet_NaN(),
24801      double z = std::numeric_limits<double>::quiet_NaN(),
24802      double m = std::numeric_limits<double>::quiet_NaN(),
24803      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
24804    );
24805#else
24806    QgsPoint( SIP_PYOBJECT x SIP_TYPEHINT( Optional[Union[QgsPoint, QPointF, float]] ) = Py_None, SIP_PYOBJECT y SIP_TYPEHINT( Optional[float] ) = Py_None, SIP_PYOBJECT z SIP_TYPEHINT( Optional[float] ) = Py_None, SIP_PYOBJECT m SIP_TYPEHINT( Optional[float] ) = Py_None, SIP_PYOBJECT wkbType SIP_TYPEHINT( Optional[int] ) = Py_None ) [( double x = 0.0, double y = 0.0, double z = 0.0, double m = 0.0, Qgis::WkbType wkbType = Qgis::WkbType::Unknown )];
24807    % MethodCode
24808    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24809    {
24810      int state;
24811      sipIsErr = 0;
24812      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24813      if ( !sipIsErr )
24814      {
24815        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
24816      }
24817      sipReleaseType( p, sipType_QgsPointXY, state );
24818    }
24819    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
24820    {
24821      int state;
24822      sipIsErr = 0;
24823
24824      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
24825      if ( !sipIsErr )
24826      {
24827        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
24828      }
24829      sipReleaseType( p, sipType_QPointF, state );
24830    }
24831    else if (
24832      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
24833      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
24834      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
24835      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
24836    {
24837      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
24838      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
24839      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
24840      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
24841      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
24842      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
24843    }
24844    else // Invalid ctor arguments
24845    {
24846      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
24847      sipIsErr = 1;
24848    }
24849    % End
24850#endif
24851
24852    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
24853    explicit QgsPoint( QPointF p ) SIP_SKIP;
24854    explicit QgsPoint(
24855      Qgis::WkbType wkbType,
24856      double x = std::numeric_limits<double>::quiet_NaN(),
24857      double y = std::numeric_limits<double>::quiet_NaN(),
24858      double z = std::numeric_limits<double>::quiet_NaN(),
24859      double m = std::numeric_limits<double>::quiet_NaN()
24860    ) SIP_SKIP;
24861    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24862    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
24863    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24864#ifndef SIP_RUN
24865  private:
24866    bool fuzzyHelper(
24867      double epsilon,
24868      const AbstractGeometry &other,
24869      bool is3DFlag,
24870      bool isMeasureFlag
24871    ) const
24872    {
24873      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
24874    }
24875#endif
24876};
24877class Ordinary : public Base { public: Ordinary(); };
24878class API_EXPORT Plain { public: Plain(); };
24879class API_EXPORT : public Base {};
24880class
24881PN_CPP_CLASS_EXTERN Sender : public Link {
24882    Sender();
24883    struct impl;
24884    struct impl& get_impl() const;
24885};
24886class thread_ctx_t {};
24887class ctx_t ZMQ_FINAL : public thread_ctx_t {
24888    bool start();
24889};
24890"#;
24891        let mut parser = tree_sitter::Parser::new();
24892        parser
24893            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24894            .unwrap();
24895        let tree = parser.parse(source, None).unwrap();
24896        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
24897        let parsed = parse_cpp_file(&file, source, &tree);
24898        let declarations = parsed.declarations();
24899
24900        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
24901            assert!(
24902                declarations
24903                    .iter()
24904                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
24905                "missing recovered class {expected}: {declarations:#?}"
24906            );
24907        }
24908        let qgs_point = declarations
24909            .iter()
24910            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
24911            .expect("recovered QgsPoint class");
24912        assert_eq!(
24913            parsed.raw_supertypes.get(qgs_point),
24914            Some(&vec!["AbstractGeometry".to_string()]),
24915            "single-base export recovery must retain its displaced base"
24916        );
24917        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
24918        assert!(
24919            parsed
24920                .navigation_ranges
24921                .get(qgs_point)
24922                .is_some_and(|ranges| {
24923                    !ranges.is_empty()
24924                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
24925                }),
24926            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
24927            parsed.navigation_ranges.get(qgs_point)
24928        );
24929        let sender = declarations
24930            .iter()
24931            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
24932            .expect("recovered Sender class");
24933        assert_eq!(
24934            parsed.raw_supertypes.get(sender),
24935            Some(&vec!["Link".to_string()]),
24936            "post-declarator export recovery must retain its displaced base"
24937        );
24938        let recovered_member = declarations
24939            .iter()
24940            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
24941            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
24942        assert_eq!(
24943            parsed
24944                .signature_metadata
24945                .get(recovered_member)
24946                .and_then(|metadata| metadata.first())
24947                .and_then(SignatureMetadata::callable_linkage),
24948            Some(CallableLinkage::External),
24949            "a named recovered class's members have external linkage"
24950        );
24951        let ctx = declarations
24952            .iter()
24953            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
24954            .expect("recovered ctx_t class");
24955        assert_eq!(
24956            parsed.raw_supertypes.get(ctx),
24957            Some(&vec!["thread_ctx_t".to_string()]),
24958            "postfix export-macro recovery must retain its displaced base"
24959        );
24960        assert!(
24961            declarations.iter().any(|unit| {
24962                unit.is_function()
24963                    && unit.fq_name() == "QgsPoint.QgsPoint"
24964                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
24965            }),
24966            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
24967        );
24968        assert!(
24969            declarations.iter().all(|unit| {
24970                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
24971            }),
24972            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
24973        );
24974    }
24975
24976    #[test]
24977    #[hardwon(
24978        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24979    )]
24980    fn function_like_export_macro_classes_keep_names_and_base_edges() {
24981        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
24982        // error: a plain body, a single base, `final` without a base, `final`
24983        // with one base, and `final` with a comma-separated base list.
24984        let source = r#"
24985namespace api {
24986class PROJECT_PUBLIC_API(2, 0) Prelude {
24987  public:
24988    Prelude();
24989};
24990class PROJECT_PUBLIC_API(2, 0) Base {
24991  public:
24992    Base(int value);
24993};
24994class PROJECT_PUBLIC_API(2, 0) Mixin {
24995  public:
24996    Mixin();
24997};
24998class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
24999  public:
25000    Adopted(int value);
25001};
25002class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25003  public:
25004    Derived(int value);
25005};
25006class PROJECT_PUBLIC_API(2, 0) Solo final {
25007  public:
25008    Solo();
25009};
25010class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25011  public:
25012    Blended(int value);
25013};
25014class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25015  public:
25016    Woven(int value);
25017};
25018} // namespace api
25019"#;
25020        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25021        let declarations = parsed.declarations();
25022        let class_named = |name: &str| {
25023            declarations
25024                .iter()
25025                .find(|unit| unit.is_class() && unit.fq_name() == name)
25026                .unwrap_or_else(|| {
25027                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25028                })
25029        };
25030        let base = class_named("api.Base");
25031        class_named("api.Prelude");
25032        class_named("api.Mixin");
25033
25034        assert_eq!(
25035            parsed.raw_supertypes.get(class_named("api.Adopted")),
25036            Some(&vec!["Base".to_string()])
25037        );
25038        assert_eq!(
25039            parsed.raw_supertypes.get(class_named("api.Derived")),
25040            Some(&vec!["Base".to_string()])
25041        );
25042        assert_eq!(
25043            parsed.raw_supertypes.get(class_named("api.Solo")),
25044            None,
25045            "a final class without a base list must not invent a supertype"
25046        );
25047        assert_eq!(
25048            parsed.raw_supertypes.get(class_named("api.Blended")),
25049            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25050        );
25051        assert_eq!(
25052            parsed.raw_supertypes.get(class_named("api.Woven")),
25053            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25054        );
25055        assert!(
25056            declarations
25057                .iter()
25058                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25059            "the export macro must not become a declaration: {declarations:#?}"
25060        );
25061        assert!(
25062            declarations.iter().all(|unit| !matches!(
25063                unit.identifier(),
25064                "final" | "public" | "protected" | "private"
25065            )),
25066            "the head specifiers must not become declarations: {declarations:#?}"
25067        );
25068        assert!(
25069            parsed
25070                .navigation_ranges
25071                .get(base)
25072                .is_some_and(|ranges| !ranges.is_empty()),
25073            "the recovered base must retain a navigable declaration range"
25074        );
25075    }
25076
25077    #[test]
25078    #[hardwon(
25079        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25080    )]
25081    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25082        // #2557: the class name is the last identifier before the head ends
25083        // (`final`, the base clause `:`, or the body), whatever its spelling.
25084        // A class named in capitals (`X509_CA`) is a class, and an object-like
25085        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
25086        // the control whose spelling never mattered.
25087        let source = r#"
25088namespace api {
25089class PROJECT_PUBLIC_API(2, 0) Base {
25090  public:
25091    Base();
25092};
25093class PROJECT_PUBLIC_API(2, 0) Mixin {
25094  public:
25095    Mixin();
25096};
25097class PROJECT_PUBLIC_API(2, 0) Name {
25098  public:
25099    Name();
25100};
25101class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25102  public:
25103    X509_CA();
25104};
25105class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25106  public:
25107    HSS_LMS_KEY();
25108};
25109class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25110  public:
25111    GOST_3410();
25112};
25113class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25114  public:
25115    PKCS11_RSA();
25116};
25117class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25118  public:
25119    Plain();
25120};
25121class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25122  public:
25123    Decorated();
25124};
25125class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25126  public:
25127    Layered();
25128};
25129} // namespace api
25130"#;
25131        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25132        let declarations = parsed.declarations();
25133        let class_named = |name: &str| {
25134            declarations
25135                .iter()
25136                .find(|unit| unit.is_class() && unit.fq_name() == name)
25137                .unwrap_or_else(|| {
25138                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25139                })
25140        };
25141        for (name, bases) in [
25142            ("api.Name", None),
25143            ("api.X509_CA", None),
25144            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25145            ("api.GOST_3410", Some(vec!["Base"])),
25146            ("api.PKCS11_RSA", None),
25147            ("api.Plain", None),
25148            ("api.Decorated", Some(vec!["Base"])),
25149            ("api.Layered", Some(vec!["Base", "Mixin"])),
25150        ] {
25151            let expected =
25152                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
25153            assert_eq!(
25154                parsed.raw_supertypes.get(class_named(name)),
25155                expected.as_ref(),
25156                "{name}"
25157            );
25158        }
25159        assert!(
25160            declarations.iter().all(|unit| !matches!(
25161                unit.identifier(),
25162                "PROJECT_PUBLIC_API"
25163                    | "OTHER_MACRO"
25164                    | "FIRST_MACRO"
25165                    | "SECOND_MACRO"
25166                    | "final"
25167                    | "public"
25168            )),
25169            "macros and head specifiers must not become declarations: {declarations:#?}"
25170        );
25171    }
25172
25173    #[test]
25174    #[hardwon(
25175        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25176    )]
25177    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25178        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
25179        // head, the body's `{` and its first member in one declaration-scope
25180        // ERROR, scatters the remaining members across the container's
25181        // siblings, and ends the container on the class's own `}`. The class
25182        // was lost and the macro minted a class of its own.
25183        let source = r#"
25184namespace api {
25185
25186/**
25187* Doc comment
25188*/
25189class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25190   public:
25191      /**
25192      * Construct from a point.
25193      */
25194      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25195
25196#if defined(PROJECT_HAS_LEGACY_POINT)
25197      /**
25198      * Construct from a legacy point.
25199      */
25200      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25201#endif
25202
25203      std::string algo_name() const override;
25204
25205      AlgorithmIdentifier algorithm_identifier() const override;
25206};
25207
25208}
25209"#;
25210        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25211        let declarations = parsed.declarations();
25212        let class = declarations
25213            .iter()
25214            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25215            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25216        assert_eq!(
25217            parsed.raw_supertypes.get(class),
25218            Some(&vec!["BaseKey".to_string()]),
25219            "the virtual base is the class's base: {declarations:#?}"
25220        );
25221        for member in [
25222            "api.VirtualBased.algo_name",
25223            "api.VirtualBased.algorithm_identifier",
25224        ] {
25225            assert!(
25226                declarations
25227                    .iter()
25228                    .any(|unit| unit.is_function() && unit.fq_name() == member),
25229                "{member} must be owned by the recovered class: {declarations:#?}"
25230            );
25231        }
25232        assert!(
25233            declarations
25234                .iter()
25235                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25236            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25237        );
25238    }
25239
25240    #[test]
25241    #[hardwon(
25242        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25243    )]
25244    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25245        // #2924: object-like macro lines before the head demote the `class`
25246        // keyword to a bare identifier and make the macro invocation an
25247        // `init_declarator`, so the head arrives as a `declaration` rather than
25248        // as an ERROR carrying a `class_specifier`. The name is still the last
25249        // identifier before `final`, and `virtual` is a base specifier rather
25250        // than a base.
25251        let source = r#"
25252namespace api {
25253
25254DIAGNOSTIC_PUSH
25255DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25256
25257class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25258                                               public virtual api::Inner::Key {
25259   public:
25260      std::string algo_name() const override;
25261};
25262
25263DIAGNOSTIC_POP
25264
25265}
25266"#;
25267        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25268        let declarations = parsed.declarations();
25269        let class = declarations
25270            .iter()
25271            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25272            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25273        assert_eq!(
25274            parsed.raw_supertypes.get(class),
25275            Some(&vec![
25276                "api::Outer::Key".to_string(),
25277                "api::Inner::Key".to_string()
25278            ]),
25279            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25280        );
25281        assert!(
25282            declarations
25283                .iter()
25284                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25285            "the member is owned by the recovered class: {declarations:#?}"
25286        );
25287        assert!(
25288            declarations
25289                .iter()
25290                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25291            "the macro invocation must not mint a declaration: {declarations:#?}"
25292        );
25293    }
25294
25295    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25296        format!(
25297            r#"
25298namespace api {{
25299class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25300   public:
25301      /** Return a descriptive string. */
25302      const char* what() const noexcept override {{ return m_msg.c_str(); }}
25303
25304      /** Return the type of error. */
25305      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25306
25307      /** Return an associated error code. */
25308      virtual int error_code() const noexcept {{ return 0; }}
25309
25310      /** Avoid throwing the base directly. */
25311      explicit Exception(std::string_view msg);
25312
25313      /** Avoid throwing the base directly. */
25314      Exception(const char* prefix, std::string_view msg);
25315
25316      /** Avoid throwing the base directly. */
25317      Exception(std::string_view msg, const std::exception& e);
25318
25319   private:
25320      std::string m_msg;
25321}};
25322
25323class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25324   public:
25325      explicit {name}(std::string_view msg);
25326
25327      explicit {name}(std::string_view msg, std::string_view where);
25328
25329      {name}(std::string_view msg, const std::exception& e);
25330
25331      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25332}};
25333}} // namespace api
25334"#
25335        )
25336    }
25337
25338    #[test]
25339    #[hardwon(
25340        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25341    )]
25342    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25343        // The class embedded in a preceding malformed body follows the same
25344        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
25345        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25346            let source = function_like_export_class_fixture(head, name);
25347            let mut parser = tree_sitter::Parser::new();
25348            parser
25349                .set_language(&tree_sitter_cpp::LANGUAGE.into())
25350                .expect("set C++ grammar");
25351            let tree = parser.parse(&source, None).expect("parse fixture");
25352            let mut embedded = Vec::new();
25353            let mut stack = vec![tree.root_node()];
25354            while let Some(node) = stack.pop() {
25355                embedded.extend(
25356                    recover_embedded_function_like_export_classes(node, &source)
25357                        .into_iter()
25358                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
25359                );
25360                let mut cursor = node.walk();
25361                stack.extend(node.named_children(&mut cursor));
25362            }
25363            assert!(
25364                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
25365                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
25366                tree.root_node().to_sexp()
25367            );
25368            assert!(
25369                embedded
25370                    .iter()
25371                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
25372                "{head}: the object-like macro is not a class: {embedded:#?}"
25373            );
25374
25375            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25376            let declarations = parsed.declarations();
25377            let class = declarations
25378                .iter()
25379                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25380                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25381            assert_eq!(
25382                parsed.raw_supertypes.get(class),
25383                Some(&vec!["Exception".to_string()]),
25384                "{head}"
25385            );
25386            assert!(
25387                declarations
25388                    .iter()
25389                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
25390                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25391            );
25392        }
25393    }
25394
25395    #[test]
25396    #[hardwon(
25397        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25398    )]
25399    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25400        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
25401        // `class MACRO(3, 6) Name final :` in the error, the first base as the
25402        // `type` of the following declaration, and closes that declaration with
25403        // a zero-width `MISSING identifier`. Reading the head by position must
25404        // not take that missing node for the class name: an empty name panics
25405        // at `FqName` construction, which took the Botan corpus replay down
25406        // (#2557). The head's tokens straddle the two nodes, so the positional
25407        // read spans both and recovers the class rather than leaving the
25408        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
25409        let source = r#"
25410namespace api {
25411class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25412                                                    public virtual Botan::EC_PublicKey {
25413   public:
25414      std::string algo_name() const override { return "ECDSA"; }
25415};
25416} // namespace api
25417"#;
25418        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25419        let declarations = parsed.declarations();
25420        assert!(
25421            declarations
25422                .iter()
25423                .all(|unit| !unit.identifier().is_empty()),
25424            "no declaration may carry an empty name: {declarations:#?}"
25425        );
25426        assert!(
25427            declarations
25428                .iter()
25429                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25430            "macros and head specifiers must not become declarations: {declarations:#?}"
25431        );
25432        let class = declarations
25433            .iter()
25434            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25435            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25436        assert_eq!(
25437            parsed.raw_supertypes.get(class),
25438            Some(&vec![
25439                "Botan::TPM2::PublicKey".to_string(),
25440                "Botan::EC_PublicKey".to_string()
25441            ]),
25442            "both qualified virtual bases are bases: {declarations:#?}"
25443        );
25444        let source_facts = parsed.source_facts.as_ref().unwrap();
25445        let declaration = parsed
25446            .source_declaration_units
25447            .iter()
25448            .find(|(_, unit)| unit == class)
25449            .map(|(declaration, _)| *declaration)
25450            .expect("recovered class source bridge");
25451        let bases = source_facts
25452            .cpp
25453            .as_ref()
25454            .unwrap()
25455            .declarations
25456            .iter()
25457            .find(|fact| fact.declaration == declaration)
25458            .expect("recovered class source fact")
25459            .bases
25460            .clone();
25461        assert_eq!(
25462            bases
25463                .iter()
25464                .map(|base| base.components.clone())
25465                .collect::<Vec<_>>(),
25466            vec![
25467                vec!["Botan", "TPM2", "PublicKey"],
25468                vec!["Botan", "EC_PublicKey"],
25469            ],
25470            "generalized recovery must publish structured base components"
25471        );
25472        assert!(
25473            bases.iter().all(|base| base.is_virtual),
25474            "generalized recovery must preserve virtual base qualifiers"
25475        );
25476        assert!(
25477            declarations
25478                .iter()
25479                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25480            "the head must not mint a macro-named class: {declarations:#?}"
25481        );
25482        let mixed_source = source.replace(
25483            "public virtual Botan::EC_PublicKey",
25484            "public Botan::EC_PublicKey",
25485        );
25486        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25487        let qualifiers = mixed
25488            .source_facts
25489            .as_ref()
25490            .unwrap()
25491            .cpp
25492            .as_ref()
25493            .unwrap()
25494            .declarations
25495            .iter()
25496            .flat_map(|declaration| &declaration.bases)
25497            .map(|base| base.is_virtual)
25498            .collect::<Vec<_>>();
25499        assert_eq!(
25500            qualifiers,
25501            [true, false],
25502            "base qualifiers must not cross the comma"
25503        );
25504    }
25505
25506    #[test]
25507    #[hardwon(
25508        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25509    )]
25510    fn function_like_export_class_survives_a_preceding_malformed_body() {
25511        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25512        let mut parser = tree_sitter::Parser::new();
25513        parser
25514            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25515            .expect("set C++ grammar");
25516        let tree = parser.parse(&source, None).expect("parse fixture");
25517        let mut stack = vec![tree.root_node()];
25518        let mut saw_embedded_shape = false;
25519        while let Some(node) = stack.pop() {
25520            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
25521                .iter()
25522                .any(|recovered| recovered.name == "Invalid_Argument");
25523            let mut cursor = node.walk();
25524            stack.extend(node.named_children(&mut cursor));
25525        }
25526        assert!(
25527            saw_embedded_shape,
25528            "fixture must retain the embedded error geometry: {}",
25529            tree.root_node().to_sexp()
25530        );
25531
25532        let parsed = parse_cpp_file(
25533            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25534            &source,
25535            &tree,
25536        );
25537        let declarations = parsed.declarations();
25538        let exception = declarations
25539            .iter()
25540            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25541            .expect("qualified-base export class");
25542        let invalid = declarations
25543            .iter()
25544            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25545            .expect("class embedded in the preceding malformed body");
25546
25547        assert_eq!(
25548            parsed.raw_supertypes.get(exception),
25549            Some(&vec!["std::exception".to_string()])
25550        );
25551        assert_eq!(
25552            parsed.raw_supertypes.get(invalid),
25553            Some(&vec!["Exception".to_string()])
25554        );
25555        assert!(
25556            parsed.materialization_records.iter().any(|record| matches!(
25557                record,
25558                MaterializationRecord::RecoveredDeclaration { unit, .. }
25559                    if unit == invalid
25560            )),
25561            "the embedded class must retain recovery provenance: {:#?}",
25562            parsed.materialization_records
25563        );
25564    }
25565
25566    #[test]
25567    #[hardwon(
25568        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25569    )]
25570    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25571        let source = r#"
25572public:
25573   explicit Lookup_Error(std::string_view err) : Exception(err) {}
25574
25575   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25576"#;
25577        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25578            .expect("reparse merged constructor body");
25579        let (range, body) =
25580            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25581                .unwrap_or_else(|| {
25582                    panic!(
25583                        "the merged constructor must retain its structured declarator/body: {}",
25584                        tree.root_node().to_sexp()
25585                    )
25586                });
25587        assert_eq!(
25588            source.get(range).expect("constructor range"),
25589            "Lookup_Error(std::string_view err) : Exception(err) {}"
25590        );
25591        assert_eq!(node_text(body, source), "{}");
25592    }
25593
25594    #[test]
25595    #[hardwon(
25596        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25597    )]
25598    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25599        let positive_source =
25600            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
25601        let mut parser = tree_sitter::Parser::new();
25602        parser
25603            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25604            .unwrap();
25605        let positive_tree = parser.parse(positive_source, None).unwrap();
25606        assert!(cpp_reparsed_members_are_indexable(
25607            positive_tree.root_node(),
25608            positive_source
25609        ));
25610
25611        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25612        let negative_tree = parser.parse(negative_source, None).unwrap();
25613        assert!(!cpp_reparsed_members_are_indexable(
25614            negative_tree.root_node(),
25615            negative_source
25616        ));
25617    }
25618
25619    #[test]
25620    #[hardwon(
25621        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25622    )]
25623    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
25624        let copy_control_source = r#"
25625public:
25626    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
25627    explicit Token(const Token* tok);
25628    ~Token();
25629    Token* astOperand1() { return nullptr; }
25630"#;
25631        let constraint_source = r#"
25632private:
25633    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25634    static T *tokAtImpl(T *tok, int index) {
25635        return tok;
25636    }
25637
25638    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25639    static T *linkAtImpl(T *tok, int index) {
25640        return tok;
25641    }
25642
25643public:
25644    int late() const { return 1; }
25645"#;
25646        let mut parser = tree_sitter::Parser::new();
25647        parser
25648            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25649            .unwrap();
25650        let copy_control_tree = parser
25651            .parse(copy_control_source, None)
25652            .expect("parse copy-control fixture");
25653        assert!(
25654            copy_control_tree.root_node().has_error(),
25655            "fixture must exercise adjacent copy-control recovery"
25656        );
25657        assert!(
25658            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
25659            "a complete late getter must remain recoverable after adjacent copy-control declarations"
25660        );
25661        let mut cursor = copy_control_tree.root_node().walk();
25662        assert!(
25663            copy_control_tree
25664                .root_node()
25665                .named_children(&mut cursor)
25666                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
25667            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
25668            copy_control_tree.root_node().to_sexp()
25669        );
25670        let constraint_tree = parser
25671            .parse(constraint_source, None)
25672            .expect("parse constraint-macro fixture");
25673        assert!(constraint_tree.root_node().has_error());
25674        assert!(
25675            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
25676            "complete constraint-macro members must not hide a later ordinary member"
25677        );
25678        let mut cursor = constraint_tree.root_node().walk();
25679        assert!(
25680            constraint_tree
25681                .root_node()
25682                .named_children(&mut cursor)
25683                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
25684                    child,
25685                    constraint_source
25686                )),
25687            "fixture must retain the split constraint-macro prefix/function geometry"
25688        );
25689    }
25690
25691    #[test]
25692    #[hardwon(
25693        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25694    )]
25695    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
25696        let source = r#"
25697struct Analyzer {
25698    struct Action {
25699        Action() = default;
25700        Action(const Action&) = default;
25701        Action& operator=(const Action& rhs) & = default;
25702
25703        template<class T,
25704                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
25705                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
25706        // NOLINTNEXTLINE(google-explicit-constructor)
25707        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
25708        {}
25709
25710        enum : std::uint16_t { None = 0, Read = (1 << 0) };
25711        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
25712
25713    private:
25714        unsigned int mFlag{};
25715    };
25716
25717    enum class Direction : unsigned char { Forward, Reverse };
25718    virtual Action analyze(Direction d) const = 0;
25719};
25720"#;
25721        let mut parser = tree_sitter::Parser::new();
25722        parser
25723            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25724            .unwrap();
25725        let tree = parser.parse(source, None).unwrap();
25726        assert!(tree.root_node().has_error());
25727        let root = tree.root_node();
25728        let outer = root
25729            .named_children(&mut root.walk())
25730            .find(|child| child.kind() == "ERROR")
25731            .expect("fragmented Analyzer prefix");
25732        let outer_recovered =
25733            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
25734        assert_eq!(outer_recovered.name, "Analyzer");
25735        let outer_tree = cpp_reparse_fragmented_class_body(
25736            source,
25737            outer_recovered.body.reparse_start,
25738            outer_recovered.body.reparse_end,
25739        )
25740        .expect("reparse Analyzer body");
25741        let outer_root = outer_tree.root_node();
25742        let action_prefix = outer_root
25743            .named_children(&mut outer_root.walk())
25744            .find(|child| child.kind() == "ERROR")
25745            .expect("fragmented Action prefix");
25746        let action_recovered = fragmented_class_body(action_prefix, source)
25747            .expect("structured Action fragment boundary");
25748        assert_eq!(action_recovered.name, "Action");
25749        let action_tree = cpp_reparse_fragmented_class_body(
25750            source,
25751            action_recovered.body.reparse_start,
25752            action_recovered.body.reparse_end,
25753        )
25754        .expect("reparse Action body");
25755        let action_root = action_tree.root_node();
25756        let macro_prefix = action_root
25757            .named_children(&mut action_root.walk())
25758            .find(|child| child.kind() == "ERROR")
25759            .expect("constraint macro prefix");
25760        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
25761            .expect("structured template macro prefix");
25762        let macro_companion =
25763            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
25764        assert!(
25765            cpp_reparsed_template_macro_constructor_companion_is_indexable(
25766                macro_companion,
25767                macro_parameter,
25768                source,
25769            ),
25770            "split constrained constructor must be admitted: {}",
25771            macro_companion.to_sexp()
25772        );
25773        assert!(
25774            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
25775            "complete Action body must pass the recovery gate: {}",
25776            action_tree.root_node().to_sexp()
25777        );
25778        assert!(
25779            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
25780            "complete Analyzer body must pass the recovery gate: {}",
25781            outer_tree.root_node().to_sexp()
25782        );
25783        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
25784        let parsed = parse_cpp_file(&file, source, &tree);
25785        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
25786            assert!(
25787                parsed
25788                    .declarations()
25789                    .iter()
25790                    .any(|unit| unit.fq_name() == expected),
25791                "missing recovered declaration {expected}: {:#?}",
25792                parsed.declarations()
25793            );
25794        }
25795        assert!(
25796            parsed
25797                .declarations()
25798                .iter()
25799                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
25800            "nested members must not remain flattened: {:#?}",
25801            parsed.declarations()
25802        );
25803    }
25804
25805    #[test]
25806    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
25807    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
25808        let source = "value() = default;\nint value();\n";
25809        let tree = crate::diagnostics::parse_cpp_tree(source);
25810        let root = tree.root_node();
25811        assert!(
25812            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
25813            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
25814            root.to_sexp()
25815        );
25816        assert!(
25817            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
25818            "a return-typed same-name declaration is not a constructor witness: {}",
25819            root.to_sexp()
25820        );
25821
25822        let damaged_source = r#"
25823value(int x) {
25824  broken(
25825}
25826value() = default;
25827int value();
25828"#;
25829        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
25830        let damaged_root = damaged_tree.root_node();
25831        // The macro-declaration grammar absorbs the whole damaged witness
25832        // into one errorful `function_definition`, so the default-assignment
25833        // expression lives inside the bogus body rather than as a top-level
25834        // sibling. The gate's answer must not change: the assignment is not
25835        // the class-named call, and the errorful same-name function is not a
25836        // separate constructor witness.
25837        let mut damaged_stack = vec![damaged_root];
25838        let mut damaged_assignment = None;
25839        while let Some(node) = damaged_stack.pop() {
25840            if node.kind() == "expression_statement" {
25841                damaged_assignment = Some(node);
25842                break;
25843            }
25844            let mut cursor = node.walk();
25845            damaged_stack.extend(node.named_children(&mut cursor));
25846        }
25847        let damaged_assignment =
25848            damaged_assignment.expect("damaged witness probe retains the assignment expression");
25849        let mut damaged_cursor = damaged_root.walk();
25850        let damaged_members = damaged_root
25851            .named_children(&mut damaged_cursor)
25852            .collect::<Vec<_>>();
25853        assert!(
25854            !cpp_reparsed_defaulted_constructor_is_indexable(
25855                damaged_assignment,
25856                &damaged_members,
25857                Some("value"),
25858                damaged_source,
25859            ),
25860            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
25861            damaged_root.to_sexp()
25862        );
25863        assert!(
25864            !cpp_reparsed_members_are_indexable_for_class(
25865                damaged_root,
25866                damaged_source,
25867                Some("value")
25868            ),
25869            "the damaged witness must not pass the member gate either: {}",
25870            damaged_root.to_sexp()
25871        );
25872    }
25873
25874    #[test]
25875    #[hardwon(
25876        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25877    )]
25878    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
25879        let source = r#"
25880raw_hash_set& operator=(raw_hash_set&& that) {
25881  return move_assign(
25882      std::move(that),
25883      typename AllocTraits::propagate_on_container_move_assignment());
25884}
25885
25886iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
25887  return {};
25888}
25889
25890void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
25891
25892iterator insert(const_iterator hint, value_type&& value)
25893    ABSL_ATTRIBUTE_LIFETIME_BOUND {
25894  return {};
25895}
25896
25897friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
25898  return left.size() == right.size();
25899}
25900
25901static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
25902  return static_cast<slot_type*>(buffer);
25903}
25904
25905protected:
25906// Included-range recovery can attach this comment to the template prefix.
25907template <class K>
25908void AssertOnFind([[maybe_unused]] const K& key) {
25909  Check(key);
25910}
25911"#;
25912        let mut parser = tree_sitter::Parser::new();
25913        parser
25914            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25915            .unwrap();
25916        let tree = parser.parse(source, None).unwrap();
25917        assert!(
25918            tree.root_node().has_error(),
25919            "the fixture must exercise tree-sitter's errorful member shapes"
25920        );
25921        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
25922
25923        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
25924        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
25925        assert!(!cpp_reparsed_members_are_indexable(
25926            incomplete_tree.root_node(),
25927            incomplete_source
25928        ));
25929
25930        let outside_error_source = "int foo() stray_attribute {}\n";
25931        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
25932        assert!(outside_error_tree.root_node().has_error());
25933        assert!(!cpp_reparsed_members_are_indexable(
25934            outside_error_tree.root_node(),
25935            outside_error_source
25936        ));
25937
25938        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
25939        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
25940        assert!(!cpp_reparsed_members_are_indexable(
25941            variable_initializer_tree.root_node(),
25942            variable_initializer_source
25943        ));
25944    }
25945
25946    #[test]
25947    #[hardwon(
25948        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25949    )]
25950    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
25951        let positive_source = r#"
25952std::pair<iterator, bool> insert(init_type&& value)
25953    ABSL_ATTRIBUTE_LIFETIME_BOUND
25954#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
25955  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
25956#endif
25957{
25958  return emplace(std::move(value));
25959}
25960"#;
25961        let mut parser = tree_sitter::Parser::new();
25962        parser
25963            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25964            .unwrap();
25965        let positive_tree = parser.parse(positive_source, None).unwrap();
25966        assert!(
25967            positive_tree.root_node().has_error(),
25968            "the fixture must exercise the split attribute/requires shape"
25969        );
25970        assert!(cpp_reparsed_members_are_indexable(
25971            positive_tree.root_node(),
25972            positive_source
25973        ));
25974
25975        let template_return_source = r#"
25976pair<int> insert(init_type&& value)
25977    ABSL_ATTRIBUTE_LIFETIME_BOUND
25978#if LANGUAGE_LEVEL >= 202002L
25979  requires(!Predicate<init_type>::value)
25980#endif
25981// Attributes and the function body may be separated by comments.
25982{
25983  return {};
25984}
25985"#;
25986        let template_return_tree = parser.parse(template_return_source, None).unwrap();
25987        assert!(
25988            cpp_reparsed_members_are_indexable(
25989                template_return_tree.root_node(),
25990                template_return_source
25991            ),
25992            "template-return attribute/requires tree: {}",
25993            template_return_tree.root_node().to_sexp()
25994        );
25995
25996        let no_body_source = r#"
25997std::pair<iterator, bool> insert(init_type&& value)
25998    ABSL_ATTRIBUTE_LIFETIME_BOUND
25999#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26000  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26001#endif
26002+ 0;
26003"#;
26004        let no_body_tree = parser.parse(no_body_source, None).unwrap();
26005        assert!(!cpp_reparsed_members_are_indexable(
26006            no_body_tree.root_node(),
26007            no_body_source
26008        ));
26009
26010        let extra_payload_source = r#"
26011pair<int> insert(init_type&& value)
26012    ABSL_ATTRIBUTE_LIFETIME_BOUND
26013#if LANGUAGE_LEVEL >= 202002L
26014  int unrelated;
26015  requires(Predicate<init_type>::value)
26016#endif
26017{
26018  return {};
26019}
26020"#;
26021        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26022        assert!(!cpp_reparsed_members_are_indexable(
26023            extra_payload_tree.root_node(),
26024            extra_payload_source
26025        ));
26026
26027        let variable_initializer_source = r#"
26028int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26029#if LANGUAGE_LEVEL >= 202002L
26030  requires(true)
26031#endif
26032{
26033  bad;
26034}
26035"#;
26036        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26037        assert!(!cpp_reparsed_members_are_indexable(
26038            variable_initializer_tree.root_node(),
26039            variable_initializer_source
26040        ));
26041    }
26042
26043    #[test]
26044    #[hardwon(
26045        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26046    )]
26047    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26048        let source = r#"namespace absl {
26049ABSL_NAMESPACE_BEGIN namespace container_internal {
26050
26051class raw_hash_set : public Base {
26052 public:
26053  using value_type = int;
26054
26055  template <class U,
26056            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26057  void insert(U value) { (void)value; }
26058
26059  struct InsertSlot {
26060    raw_hash_set& s;
26061  };
26062};
26063
26064}
26065ABSL_NAMESPACE_END
26066}"#;
26067        let mut parser = tree_sitter::Parser::new();
26068        parser
26069            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26070            .unwrap();
26071        let tree = parser.parse(source, None).unwrap();
26072        let root = tree.root_node();
26073        let outer_namespace = root
26074            .named_children(&mut root.walk())
26075            .find(|child| child.kind() == "namespace_definition")
26076            .expect("outer absl namespace");
26077        let declaration_list = outer_namespace
26078            .child_by_field_name("body")
26079            .expect("outer namespace body");
26080        let sentinel_function = declaration_list
26081            .named_children(&mut declaration_list.walk())
26082            .find(|child| child.kind() == "function_definition")
26083            .expect("malformed namespace sentinel function");
26084        let ancestry = ParentIndex::new(root);
26085        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26086            .expect("structured nested namespace sentinel");
26087        let fragmented =
26088            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26089                .expect("fragmented raw_hash_set class");
26090        assert_eq!(fragmented.class_node.kind(), "ERROR");
26091        assert_eq!(fragmented.name, "raw_hash_set");
26092        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26093
26094        let outer_scope =
26095            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26096        let mut outer_siblings = Vec::new();
26097        push_cpp_sentinel_sibling_classes(
26098            &mut outer_siblings,
26099            declaration_list,
26100            sentinel.function,
26101            &outer_scope,
26102            source,
26103            &ancestry,
26104        );
26105        let [outer_shadow] = outer_siblings.as_slice() else {
26106            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26107        };
26108        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26109        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26110
26111        let field = "    raw_hash_set& s;";
26112        let start = source.find(field).expect("InsertSlot field") + 4;
26113        let node = root
26114            .descendant_for_byte_range(start, start + "raw_hash_set".len())
26115            .expect("raw_hash_set type node");
26116        let recovered = cpp_sentinel_recovered_classes(root, source);
26117        let [deep_class] = recovered.as_slice() else {
26118            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26119        };
26120        assert_eq!(
26121            deep_class.namespace_scope_components,
26122            vec!["absl", "container_internal"]
26123        );
26124        assert_eq!(
26125            deep_class.scope_components,
26126            vec!["absl", "container_internal", "raw_hash_set"]
26127        );
26128        assert!(
26129            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26130                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26131        );
26132
26133        assert_eq!(
26134            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26135            Some(vec![
26136                "absl".to_string(),
26137                "container_internal".to_string(),
26138                "raw_hash_set".to_string(),
26139                "InsertSlot".to_string(),
26140            ])
26141        );
26142
26143        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26144        let parsed = parse_cpp_file(&file, source, &tree);
26145        let raw_hash_set = parsed
26146            .declarations()
26147            .iter()
26148            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26149            .expect("recovered raw_hash_set class");
26150        assert_eq!(
26151            raw_hash_set.fq_name(),
26152            "absl::container_internal.raw_hash_set",
26153            "the recovered declaration must publish under the deeper sentinel namespace"
26154        );
26155        assert_eq!(
26156            parsed.raw_supertypes.get(raw_hash_set),
26157            Some(&vec!["Base".to_string()]),
26158            "the structured base clause on the fragmented ERROR prefix must survive publication"
26159        );
26160        assert!(
26161            parsed.materialization_records.iter().any(|record| matches!(
26162                record,
26163                MaterializationRecord::RecoveredDeclaration { recovery, unit }
26164                    if unit == raw_hash_set && *recovery == deep_class.class_range
26165            )),
26166            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26167            parsed.materialization_records
26168        );
26169    }
26170
26171    /// Issue #2358: recording an aggregate definition must not walk the whole
26172    /// file.
26173    ///
26174    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
26175    /// class-like shape that has a body, so the removal step runs once per
26176    /// aggregate. It used to `retain` over `top_level_declarations` and over
26177    /// *every* child list in the file on each of those calls, comparing whole
26178    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
26179    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
26180    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
26181    /// tens of thousands of times over and the C forward differential never
26182    /// finished.
26183    ///
26184    /// A definition the file has not already declared removes nothing, so the
26185    /// honest cost is zero regardless of how many other aggregates surround it.
26186    /// Two sizes an order of magnitude apart pin that the count is not merely
26187    /// small but independent of the file.
26188    ///
26189    /// The declaration walk answers every ancestor question from a
26190    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
26191    /// the root for each one (#2361). Substituting the index is only safe
26192    /// because it answers the identical question, so pin that on the shapes
26193    /// this file's recovery paths care about: anonymous and named aggregates,
26194    /// nested namespaces, templates, macro-displaced declarations and the
26195    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
26196    /// too -- `Node::parent` walks the visible tree, not the named one.
26197    #[test]
26198    #[hardwon(
26199        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26200    )]
26201    fn the_parent_index_answers_what_tree_sitter_answers() {
26202        const SHAPES: [&str; 5] = [
26203            "namespace outer { namespace inner { struct Tag { int field; }; } }",
26204            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26205            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
26206            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26207            "class API Broken : public First, public Second {\n  void member();\n",
26208        ];
26209        for source in SHAPES {
26210            let mut parser = tree_sitter::Parser::new();
26211            parser
26212                .set_language(&tree_sitter_cpp::LANGUAGE.into())
26213                .unwrap();
26214            let tree = parser.parse(source, None).unwrap();
26215            let root = tree.root_node();
26216            let ancestry = ParentIndex::new(root);
26217            let mut nodes = 0usize;
26218            let mut stack = vec![root];
26219            while let Some(node) = stack.pop() {
26220                nodes += 1;
26221                assert_eq!(
26222                    node.parent().map(|parent| parent.id()),
26223                    ancestry.parent(node).map(|parent| parent.id()),
26224                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26225                );
26226                let mut cursor = node.walk();
26227                stack.extend(node.children(&mut cursor));
26228            }
26229            assert!(nodes > 1, "{source:?} produced no tree to compare");
26230        }
26231    }
26232
26233    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
26234    /// index for every ancestor edge. Asking tree-sitter directly makes each
26235    /// edge re-descend from the root, turning declaration extraction on a
26236    /// deeply nested generated header from quadratic output work into a cubic
26237    /// tree walk. Exact query counts pin the route without a machine-dependent
26238    /// wall-clock ceiling.
26239    #[test]
26240    #[hardwon(
26241        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26242    )]
26243    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26244        const DEPTH: usize = 64;
26245        let mut source = String::new();
26246        for level in 0..DEPTH {
26247            writeln!(source, "namespace n{level} {{").unwrap();
26248        }
26249        source.push_str("int deepest(int value);\n");
26250        for _ in 0..DEPTH {
26251            source.push_str("}\n");
26252        }
26253
26254        let mut parser = tree_sitter::Parser::new();
26255        parser
26256            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26257            .unwrap();
26258        let tree = parser.parse(&source, None).unwrap();
26259        let root = tree.root_node();
26260        let ancestry = ParentIndex::new(root);
26261        let mut function_declarator = None;
26262        walk_named_tree_preorder(root, true, |node| {
26263            if node.kind() == "function_declarator" {
26264                function_declarator = Some(node);
26265                WalkControl::Break
26266            } else {
26267                WalkControl::Continue
26268            }
26269        });
26270        let function_declarator = function_declarator.expect("deepest function declarator");
26271        let ancestor_count =
26272            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26273
26274        ancestry.reset_parent_query_count_for_test();
26275        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26276        assert_eq!(DEPTH, lexical_scope.len());
26277        assert_eq!(
26278            ancestor_count + 1,
26279            ancestry.parent_query_count_for_test(),
26280            "lexical-scope ancestry bypassed the parent index"
26281        );
26282
26283        ancestry.reset_parent_query_count_for_test();
26284        assert_eq!(
26285            Some(DispatchExtensibility::Closed),
26286            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26287        );
26288        assert_eq!(
26289            ancestor_count,
26290            ancestry.parent_query_count_for_test(),
26291            "dispatch ancestry bypassed the parent index"
26292        );
26293
26294        ancestry.reset_parent_query_count_for_test();
26295        assert_eq!(
26296            CallableLinkage::External,
26297            cpp_callable_linkage(function_declarator, &source, &ancestry)
26298        );
26299        assert_eq!(
26300            ancestor_count + 1,
26301            ancestry.parent_query_count_for_test(),
26302            "linkage ancestry bypassed the parent index"
26303        );
26304
26305        ancestry.reset_parent_query_count_for_test();
26306        assert!(!cpp_callable_is_structural_constructor(
26307            function_declarator,
26308            &source,
26309            &ancestry
26310        ));
26311        assert_eq!(
26312            ancestor_count + 1,
26313            ancestry.parent_query_count_for_test(),
26314            "constructor ancestry bypassed the parent index"
26315        );
26316    }
26317
26318    /// #3507: the one row-level dispatch fact C++ can state is stated only
26319    /// where the language states it -- in the class body.
26320    ///
26321    /// A call's exactness is not readable from a definition row: an
26322    /// out-of-line definition repeats no `virtual`, no `final`, and no base
26323    /// clause, and C++ inherits virtual-ness from a base that declares the
26324    /// member. Each shape below therefore has to answer for itself, and only
26325    /// a class body with no base clause at all can close a member that writes
26326    /// nothing.
26327    #[test]
26328    #[hardwon(
26329        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26330    )]
26331    fn callable_dispatch_extensibility_comes_from_the_class_body() {
26332        const SOURCE: &str = "\
26333class Plain { public: long send(long order); };
26334class Derived : public Base { public: long send(long order); };
26335class Sealed : public Base { public: long send(long order) final; };
26336class Hiding : public Base { public: long send(long order) { return order; } };
26337class Virtual { public: virtual long send(long order); };
26338namespace shop { long free_call(long order); }
26339long Plain::send(long order) { return order; }
26340";
26341        let mut parser = tree_sitter::Parser::new();
26342        parser
26343            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26344            .unwrap();
26345        let tree = parser.parse(SOURCE, None).unwrap();
26346        let root = tree.root_node();
26347        let ancestry = ParentIndex::new(root);
26348        let mut answers = Vec::new();
26349        walk_named_tree_preorder(root, true, |node| {
26350            if node.kind() != "function_declarator" {
26351                return WalkControl::Continue;
26352            }
26353            let name = cpp_function_declarator_name_node(node).expect("declarator name");
26354            answers.push((
26355                node_text(name, SOURCE).to_string(),
26356                cpp_callable_dispatch_extensibility(node, &ancestry),
26357            ));
26358            WalkControl::Continue
26359        });
26360        assert_eq!(
26361            answers,
26362            vec![
26363                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26364                ("send".to_string(), Some(DispatchExtensibility::Open)),
26365                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26366                ("send".to_string(), Some(DispatchExtensibility::Open)),
26367                ("send".to_string(), Some(DispatchExtensibility::Open)),
26368                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26369                ("Plain::send".to_string(), None),
26370            ],
26371            "the class body states the fact; a qualified definition states none"
26372        );
26373    }
26374
26375    #[test]
26376    #[hardwon(
26377        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26378    )]
26379    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26380        const DISTINCT_PER_KIND: usize = 64;
26381        let mut source = String::new();
26382        for index in 0..DISTINCT_PER_KIND {
26383            writeln!(source, "typedef int Alias{index};").unwrap();
26384        }
26385        writeln!(source, "typedef long Alias0;").unwrap();
26386        for index in 0..DISTINCT_PER_KIND {
26387            writeln!(source, "#define MACRO_{index} {index}").unwrap();
26388        }
26389        writeln!(source, "#define MACRO_0 duplicate").unwrap();
26390        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26391
26392        let mut parser = tree_sitter::Parser::new();
26393        parser
26394            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26395            .unwrap();
26396        let tree = parser.parse(&source, None).unwrap();
26397        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26398
26399        start_declaration_identity_comparison_probe();
26400        let parsed = parse_cpp_file(&file, &source, &tree);
26401        let comparisons = finish_declaration_identity_comparison_probe();
26402
26403        assert_eq!(
26404            DISTINCT_PER_KIND + 1,
26405            parsed
26406                .declarations()
26407                .iter()
26408                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26409                .count(),
26410            "every physical typedef alias declaration must be retained so \
26411             conditional branch guards stay available to the resolver"
26412        );
26413        assert_eq!(
26414            DISTINCT_PER_KIND + 1,
26415            parsed
26416                .declarations()
26417                .iter()
26418                .filter(|unit| {
26419                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26420                })
26421                .count(),
26422            "distinct macro redefinitions must remain available to temporal lookup"
26423        );
26424        assert_eq!(
26425            2,
26426            parsed
26427                .declarations()
26428                .iter()
26429                .filter(|unit| {
26430                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26431                })
26432                .count(),
26433            "function overloads must remain distinct"
26434        );
26435
26436        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26437        assert!(
26438            comparisons <= dedup_inputs * 4,
26439            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26440        );
26441    }
26442
26443    #[test]
26444    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26445    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26446        let negative_source = r#"namespace absl {
26447ABSL_NAMESPACE_BEGIN namespace container_internal {
26448template <typename T>
26449class broken {
26450 public:
26451  using value_type = T;
26452  T operator->() const { return &operator*(); }
26453}
26454}
26455"#;
26456        let mut parser = tree_sitter::Parser::new();
26457        parser
26458            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26459            .unwrap();
26460        let negative_tree = parser.parse(negative_source, None).unwrap();
26461        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26462            .expect("the negative fixture must expose the malformed class node");
26463        assert!(
26464            broken.has_error(),
26465            "the negative fixture must retain a parser error"
26466        );
26467        // The macro-declaration grammar keeps the class body closed here; the
26468        // damage is the missing `;` and the unclosed namespace around it, so
26469        // the class's incompleteness is proven by the sentinel recognizers
26470        // finding no envelope for it rather than by a shared parent boundary.
26471        let mut envelope = None;
26472        {
26473            let mut stack = vec![negative_tree.root_node()];
26474            while let Some(node) = stack.pop() {
26475                if cpp_nested_namespace_sentinel(
26476                    node,
26477                    negative_source,
26478                    &ParentIndex::new(negative_tree.root_node()),
26479                )
26480                .is_some()
26481                    || cpp_root_namespace_sentinel(
26482                        node,
26483                        negative_source,
26484                        &ParentIndex::new(negative_tree.root_node()),
26485                    )
26486                    .is_some()
26487                    || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26488                {
26489                    envelope = Some(node);
26490                    break;
26491                }
26492                stack.extend(node.named_children(&mut node.walk()));
26493            }
26494        }
26495        assert!(
26496            envelope.is_none(),
26497            "an incomplete class must not prove a sentinel envelope: {}",
26498            negative_tree.root_node().to_sexp()
26499        );
26500        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26501        assert!(
26502            recovered
26503                .iter()
26504                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26505            "an incomplete class must not borrow the namespace close: {recovered:#?}"
26506        );
26507
26508        let positive_source = r#"namespace absl {
26509ABSL_NAMESPACE_BEGIN namespace container_internal {
26510template <typename T>
26511class broken {
26512 public:
26513  using value_type = T;
26514  T operator->() const { return &operator*(); }
26515  using alias = value_type;
26516};
26517}
26518}
26519"#;
26520        let positive_tree = parser.parse(positive_source, None).unwrap();
26521        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26522            .expect("the positive fixture must expose the broken class node");
26523        assert!(
26524            broken.has_error(),
26525            "the positive fixture must retain an internal parser error"
26526        );
26527        assert!(
26528            cpp_complete_class_body_close(broken).is_some(),
26529            "the positive fixture must expose a real class body close"
26530        );
26531        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26532        assert!(
26533            recovered.iter().any(|class| {
26534                class.scope_components == ["absl", "container_internal", "broken"]
26535            }),
26536            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26537        );
26538    }
26539
26540    #[test]
26541    #[hardwon(
26542        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26543    )]
26544    fn sentinel_recovery_keeps_members_after_nested_body_close() {
26545        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26546NLOHMANN_BASIC_JSON_TPL_DECLARATION
26547class basic_json {
26548 private:
26549  union storage {
26550    int value;
26551  } data;
26552 public:
26553  using late_alias = int;
26554  late_alias value() const;
26555};
26556NLOHMANN_JSON_NAMESPACE_END
26557"#;
26558        let mut parser = tree_sitter::Parser::new();
26559        parser
26560            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26561            .unwrap();
26562        let tree = parser.parse(source, None).unwrap();
26563        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26564        let basic_json = recovered
26565            .iter()
26566            .find(|class| {
26567                class
26568                    .scope_components
26569                    .last()
26570                    .is_some_and(|name| name == "basic_json")
26571            })
26572            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26573        let late_alias = source
26574            .find("late_alias value")
26575            .expect("late alias reference");
26576        assert!(
26577            basic_json.class_range.start_byte < late_alias
26578                && late_alias < basic_json.class_range.end_byte,
26579            "the recovered class range must include members after a nested close: {basic_json:#?}"
26580        );
26581    }
26582
26583    #[test]
26584    #[hardwon(
26585        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26586    )]
26587    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26588        let source = r#"namespace absl {
26589ABSL_NAMESPACE_BEGIN namespace container_internal {
26590template <typename T>
26591struct broken {
26592  using value_type = T;
26593};
26594}
26595
26596#ifdef OWNER_DEF
26597template <typename T>
26598typename broken<T>::value_type broken<T>::method() {
26599  value_type value{};
26600  return value;
26601}
26602#endif
26603
26604namespace sibling {
26605template <typename T>
26606typename broken<T>::value_type broken<T>::other() {
26607  value_type value{};
26608  return value;
26609}
26610}
26611
26612ABSL_NAMESPACE_END
26613}
26614"#;
26615        let mut parser = tree_sitter::Parser::new();
26616        parser
26617            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26618            .unwrap();
26619        let tree = parser.parse(source, None).unwrap();
26620        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26621        let broken = recovered
26622            .iter()
26623            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
26624            .expect("the sentinel class must be recovered");
26625        let method_start = source
26626            .find("typename broken<T>::value_type broken<T>::method()")
26627            .expect("guarded sibling owner");
26628        let method_end = source[method_start..]
26629            .find("\n}")
26630            .map(|offset| method_start + offset + 2)
26631            .expect("guarded sibling owner close");
26632        assert!(
26633            broken
26634                .owner_ranges
26635                .iter()
26636                .any(|owner| owner.range.start_byte <= method_start
26637                    && method_end <= owner.range.end_byte),
26638            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
26639        );
26640        let sibling_start = source
26641            .find("typename broken<T>::value_type broken<T>::other()")
26642            .expect("nested namespace sibling owner");
26643        assert!(
26644            broken
26645                .owner_ranges
26646                .iter()
26647                .all(|owner| owner.range.start_byte > sibling_start
26648                    || owner.range.end_byte <= sibling_start),
26649            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
26650        );
26651    }
26652
26653    #[test]
26654    #[hardwon(
26655        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
26656    )]
26657    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
26658        let source = r#"#ifdef OUTER
26659namespace absl {
26660ABSL_NAMESPACE_BEGIN namespace container_internal {
26661template <typename T>
26662struct broken {
26663  using value_type = T;
26664};
26665}
26666}
26667
26668#ifdef OWNER_DEF
26669template <typename T>
26670typename broken<T>::value_type broken<T>::method() {
26671  value_type value{};
26672  return value;
26673}
26674#endif
26675#endif
26676"#;
26677        // The macro-declaration grammar keeps the sentinel envelope clean, so
26678        // the recovered-region index has nothing malformed to repair; the
26679        // declaration walk owns the recovery, and the out-of-line definition
26680        // after the missing ABSL_NAMESPACE_END must stay unowned.
26681        let parsed = parse_cpp_declarations(source, "discard-outer.h");
26682        assert!(
26683            parsed
26684                .declarations()
26685                .iter()
26686                .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
26687            "the sentinel class must be recovered: {:#?}",
26688            parsed.declarations()
26689        );
26690        let method = parsed
26691            .declarations()
26692            .iter()
26693            .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
26694            .expect("the guarded out-of-line definition");
26695        assert_eq!(
26696            method.fq_name(),
26697            "broken.method",
26698            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
26699            parsed.declarations()
26700        );
26701    }
26702
26703    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
26704    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
26705        let mut signatures = all_function_identities(parsed)
26706            .into_iter()
26707            .filter(|(name, _)| name == fq_name)
26708            .filter_map(|(_, signature)| signature)
26709            .collect::<Vec<_>>();
26710        signatures.sort();
26711        signatures.dedup();
26712        signatures
26713    }
26714
26715    #[test]
26716    #[hardwon(
26717        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
26718    )]
26719    fn callable_parameter_types_come_from_the_ast_parameter_list() {
26720        let source = r#"
26721template <typename T, ENABLE_BYTES(T)>
26722Vec256<T> DupOdd(Vec256<T> value) { return value; }
26723
26724struct Visitor {
26725  void fail(this auto const& self) {}
26726  void consume(this Visitor const& self, int count) {}
26727};
26728"#;
26729        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
26730        let dup_odd = parsed
26731            .declarations()
26732            .iter()
26733            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
26734            .expect("DupOdd declaration");
26735        assert_eq!(
26736            dup_odd.signature(),
26737            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
26738        );
26739        assert_eq!(
26740            parsed
26741                .signature_metadata
26742                .get(dup_odd)
26743                .and_then(|metadata| metadata.first())
26744                .and_then(SignatureMetadata::callable_parameter_types),
26745            Some(["Vec256<T>".to_string()].as_slice())
26746        );
26747
26748        let fail = parsed
26749            .declarations()
26750            .iter()
26751            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
26752            .expect("explicit-object member");
26753        assert_eq!(fail.signature(), Some("(this const auto &)"));
26754        let metadata = parsed
26755            .signature_metadata
26756            .get(fail)
26757            .and_then(|metadata| metadata.first())
26758            .expect("explicit-object signature metadata");
26759        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
26760        assert!(
26761            metadata
26762                .callable_arity()
26763                .is_some_and(|arity| arity.accepts(0))
26764        );
26765        let consume = parsed
26766            .declarations()
26767            .iter()
26768            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
26769            .expect("explicit-object member with an invocation argument");
26770        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
26771        let metadata = parsed
26772            .signature_metadata
26773            .get(consume)
26774            .and_then(|metadata| metadata.first())
26775            .expect("typed explicit-object signature metadata");
26776        assert_eq!(
26777            metadata.callable_parameter_types(),
26778            Some(["int".to_string()].as_slice())
26779        );
26780        assert!(
26781            metadata
26782                .callable_arity()
26783                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
26784        );
26785    }
26786
26787    #[test]
26788    #[hardwon(
26789        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
26790    )]
26791    fn trailing_qualifiers_survive_parameter_list_whitespace() {
26792        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
26793        // declarator's structure, so an out-of-line definition that spells its
26794        // parameter list with different whitespace than the declaration must
26795        // still carry it.
26796        let source = r#"
26797struct Widget {
26798  bool multiline(int settings, int supprs) const;
26799  bool doublespace(int settings, int supprs) const;
26800  bool noexcept_multiline(int settings, int supprs) noexcept;
26801  bool ref_multiline(int settings, int supprs) &&;
26802};
26803bool
26804Widget::multiline (int settings,
26805                   int supprs) const
26806{ return settings + supprs > 0; }
26807bool Widget::doublespace(int settings,  int supprs) const { return true; }
26808bool Widget::noexcept_multiline(int settings,
26809                                int supprs) noexcept { return true; }
26810bool Widget::ref_multiline(int settings,
26811                           int supprs) && { return true; }
26812"#;
26813        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
26814        assert_eq!(
26815            vec!["(int, int) const".to_string()],
26816            identity_signatures(&parsed, "Widget.multiline")
26817        );
26818        assert_eq!(
26819            vec!["(int, int) const".to_string()],
26820            identity_signatures(&parsed, "Widget.doublespace")
26821        );
26822        assert_eq!(
26823            vec!["(int, int) noexcept".to_string()],
26824            identity_signatures(&parsed, "Widget.noexcept_multiline")
26825        );
26826        assert_eq!(
26827            vec!["(int, int) &&".to_string()],
26828            identity_signatures(&parsed, "Widget.ref_multiline")
26829        );
26830    }
26831
26832    #[test]
26833    #[hardwon(
26834        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26835    )]
26836    fn macro_fragmented_plain_class_keeps_following_member_signature() {
26837        let source = r#"
26838struct CString {};
26839class CMessage {
26840public:
26841  CString GetParams(unsigned int index, unsigned int length = -1) const
26842      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
26843    return GetParamsColon(index, length);
26844  }
26845  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
26846};
26847CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
26848  return {};
26849}
26850"#;
26851        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
26852        assert_eq!(
26853            vec!["(unsigned int, unsigned int) const".to_string()],
26854            identity_signatures(&parsed, "CMessage.GetParamsColon")
26855        );
26856    }
26857
26858    #[test]
26859    #[hardwon(
26860        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
26861    )]
26862    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
26863        let source = r#"
26864#pragma once
26865#define DEMO_DEPRECATED(message)
26866namespace demo {
26867struct Base {
26868    static int aligned(int value) { return value; }
26869    int legacy(int value) const
26870        DEMO_DEPRECATED("use replacement()") { return value; }
26871    int replacement() const;
26872    void run(int value);
26873};
26874struct OtherBase {
26875    void run(int value);
26876    static int aligned(int value) { return value; }
26877};
26878struct Derived : Base {};
26879struct Override : Base {
26880    void run(int value);
26881    static int aligned(int value) { return value; }
26882};
26883struct RecoveredOverride : Base {
26884    int legacy(int value) const
26885        DEMO_DEPRECATED("use replacement()") { return value; }
26886    void run(int value);
26887};
26888struct Hidden : Base {
26889    void run(int first, int second);
26890    static int aligned(int first, int second) { return first + second; }
26891};
26892struct Ambiguous : Base, OtherBase {};
26893}
26894struct Global {};
26895"#;
26896        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
26897        let declarations = parsed.declarations();
26898        let fq_names = declarations
26899            .iter()
26900            .map(|unit| unit.fq_name())
26901            .collect::<std::collections::BTreeSet<_>>();
26902
26903        for expected in [
26904            "demo.Base",
26905            "demo.Base.aligned",
26906            "demo.Base.legacy",
26907            "demo.Base.replacement",
26908            "demo.Base.run",
26909            "demo.Derived",
26910            "demo.OtherBase",
26911            "demo.Override",
26912            "demo.RecoveredOverride",
26913            "demo.Hidden",
26914            "demo.Ambiguous",
26915            "Global",
26916        ] {
26917            assert!(
26918                fq_names.contains(expected),
26919                "missing {expected} from namespaced macro fragment: {declarations:#?}"
26920            );
26921        }
26922        assert!(
26923            !fq_names.contains("Derived"),
26924            "following class escaped its namespace: {declarations:#?}"
26925        );
26926        assert!(
26927            !fq_names.contains("demo.Global"),
26928            "global class crossed the recovered namespace boundary: {declarations:#?}"
26929        );
26930    }
26931
26932    #[test]
26933    #[hardwon(
26934        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
26935    )]
26936    fn trailing_qualifiers_still_separate_genuine_overloads() {
26937        // The qualifier must keep distinguishing the real C++ overload sets it
26938        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
26939        let source = r#"
26940struct Widget {
26941  int* slot(int index);
26942  const int* slot(int index) const;
26943  int log(int severity) &;
26944  int log(int severity) &&;
26945};
26946"#;
26947        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
26948        assert_eq!(
26949            vec!["(int)".to_string(), "(int) const".to_string()],
26950            identity_signatures(&parsed, "Widget.slot")
26951        );
26952        assert_eq!(
26953            vec!["(int) &".to_string(), "(int) &&".to_string()],
26954            identity_signatures(&parsed, "Widget.log")
26955        );
26956    }
26957
26958    #[test]
26959    #[hardwon(
26960        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
26961    )]
26962    fn virtual_specifier_is_not_part_of_the_identity_signature() {
26963        // `override` never appears on the out-of-line definition, and C++ does
26964        // not make it part of the signature, so it must not split the identity.
26965        let source = r#"
26966struct Base {
26967  virtual void run(int value) const;
26968};
26969struct Widget : Base {
26970  void run(int value) const override;
26971};
26972void Widget::run(int value) const {}
26973"#;
26974        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
26975        assert_eq!(
26976            vec!["(int) const".to_string()],
26977            identity_signatures(&parsed, "Widget.run")
26978        );
26979    }
26980
26981    #[test]
26982    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
26983    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
26984        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
26985        // the function type, so a declaration that spells `const int` and a
26986        // definition that spells `int` are one entity.
26987        let source = r#"
26988struct Widget {
26989  bool value_params(const int settings, const int supprs);
26990  void pointee_const(const int* p);
26991  void pointer_const(int* const p);
26992  void both_const(const int* const p);
26993  void reference_const(const int& p);
26994  void array_const(const int values[4]);
26995  void take(const int* p);
26996  void take(int* p);
26997};
26998bool Widget::value_params(int settings, int supprs) { return true; }
26999void Widget::pointer_const(int* p) {}
27000void Widget::both_const(const int* p) {}
27001"#;
27002        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27003        assert_eq!(
27004            vec!["(int, int)".to_string()],
27005            identity_signatures(&parsed, "Widget.value_params")
27006        );
27007        assert_eq!(
27008            vec!["(int *)".to_string()],
27009            identity_signatures(&parsed, "Widget.pointer_const")
27010        );
27011        assert_eq!(
27012            vec!["(const int *)".to_string()],
27013            identity_signatures(&parsed, "Widget.both_const")
27014        );
27015        // The const that is not top-level still distinguishes the type.
27016        assert_eq!(
27017            vec!["(const int *)".to_string()],
27018            identity_signatures(&parsed, "Widget.pointee_const")
27019        );
27020        assert_eq!(
27021            vec!["(const int &)".to_string()],
27022            identity_signatures(&parsed, "Widget.reference_const")
27023        );
27024        assert_eq!(
27025            vec!["(const int [4])".to_string()],
27026            identity_signatures(&parsed, "Widget.array_const")
27027        );
27028        assert_eq!(
27029            vec!["(const int *)".to_string(), "(int *)".to_string()],
27030            identity_signatures(&parsed, "Widget.take")
27031        );
27032    }
27033
27034    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27035        let mut parser = tree_sitter::Parser::new();
27036        parser
27037            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27038            .unwrap();
27039        let tree = parser.parse(source, None).unwrap();
27040        let start = source.find(callable_name).expect("callable declaration");
27041        let declarator =
27042            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27043        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27044    }
27045
27046    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27047        let mut shapes = comparable_shapes(source, callable_name);
27048        assert_eq!(1, shapes.len(), "{shapes:?}");
27049        match shapes.remove(0) {
27050            CppComparableSlot::Shape(shape) => shape,
27051            other => panic!("expected a comparable shape, got {other:?}"),
27052        }
27053    }
27054
27055    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27056        let mut current = shape.root();
27057        loop {
27058            match shape.node(current) {
27059                CppComparableNode::Named { .. } => return shape.node(current),
27060                CppComparableNode::Pointer { inner, .. }
27061                | CppComparableNode::Reference { inner }
27062                | CppComparableNode::Array { inner } => current = *inner,
27063                CppComparableNode::Generic { base, .. } => current = *base,
27064            }
27065        }
27066    }
27067
27068    #[test]
27069    #[hardwon(
27070        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27071    )]
27072    fn comparable_shape_keeps_pointee_const() {
27073        assert_ne!(
27074            sole_comparable_shape("void f(const char* p);", "f("),
27075            sole_comparable_shape("void f(char* p);", "f(")
27076        );
27077    }
27078
27079    #[test]
27080    #[hardwon(
27081        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27082    )]
27083    fn comparable_shape_keeps_inner_pointer_const() {
27084        assert_ne!(
27085            sole_comparable_shape("void f(int** p);", "f("),
27086            sole_comparable_shape("void f(int* const* p);", "f(")
27087        );
27088    }
27089
27090    #[test]
27091    #[hardwon(
27092        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27093    )]
27094    fn comparable_shape_drops_top_level_pointer_const() {
27095        assert_eq!(
27096            sole_comparable_shape("void f(int* const p);", "f("),
27097            sole_comparable_shape("void f(int* p);", "f(")
27098        );
27099    }
27100
27101    #[test]
27102    #[hardwon(
27103        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27104    )]
27105    fn comparable_shape_drops_top_level_base_const() {
27106        assert_eq!(
27107            sole_comparable_shape("void f(const int p);", "f("),
27108            sole_comparable_shape("void f(int p);", "f(")
27109        );
27110    }
27111
27112    #[test]
27113    #[hardwon(
27114        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27115    )]
27116    fn comparable_shape_decays_top_level_array_to_pointer() {
27117        assert_eq!(
27118            sole_comparable_shape("void f(int a[3]);", "f("),
27119            sole_comparable_shape("void f(int* a);", "f(")
27120        );
27121        assert_eq!(
27122            sole_comparable_shape("void f(int* a[3]);", "f("),
27123            sole_comparable_shape("void f(int** a);", "f(")
27124        );
27125    }
27126
27127    #[test]
27128    #[hardwon(
27129        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27130    )]
27131    fn comparable_shape_keeps_array_behind_pointer() {
27132        assert_ne!(
27133            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27134            sole_comparable_shape("struct S { void f(int** a); };", "f(")
27135        );
27136    }
27137
27138    #[test]
27139    #[hardwon(
27140        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27141    )]
27142    fn comparable_shape_records_written_name_and_lexical_scope() {
27143        let declared =
27144            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27145        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27146        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27147            panic!("named leaf");
27148        };
27149        assert_eq!(["Msg".to_string()].as_slice(), name.path());
27150        assert_eq!(
27151            ["ns".to_string(), "S".to_string()].as_slice(),
27152            name.lexical_scope()
27153        );
27154        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27155            panic!("named leaf");
27156        };
27157        assert_eq!(
27158            ["ns".to_string(), "Msg".to_string()].as_slice(),
27159            name.path()
27160        );
27161        assert!(name.lexical_scope().is_empty());
27162        assert_ne!(declared, defined);
27163    }
27164
27165    #[test]
27166    #[hardwon(
27167        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27168    )]
27169    fn comparable_shape_marks_sized_primitive_leaf() {
27170        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27171        let CppComparableNode::Named {
27172            name, primitive, ..
27173        } = comparable_named_leaf(&shape)
27174        else {
27175            panic!("named leaf");
27176        };
27177        assert!(primitive);
27178        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27179        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27180    }
27181
27182    #[test]
27183    #[hardwon(
27184        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27185    )]
27186    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27187        assert_eq!(
27188            vec![CppComparableSlot::Unstructured],
27189            comparable_shapes("void f(void (*cb)(int));", "f(")
27190        );
27191    }
27192
27193    #[test]
27194    #[hardwon(
27195        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27196    )]
27197    fn comparable_shape_reports_ellipsis_slot() {
27198        let shapes = comparable_shapes("void f(int a, ...);", "f(");
27199        assert_eq!(2, shapes.len(), "{shapes:?}");
27200        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27201    }
27202
27203    #[test]
27204    #[hardwon(
27205        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27206    )]
27207    fn comparable_shape_keeps_template_argument_const() {
27208        assert_ne!(
27209            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27210            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27211        );
27212    }
27213
27214    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
27215    /// file-scope tag that a later `struct inner *` at file scope may name.
27216    #[test]
27217    #[hardwon(
27218        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27219    )]
27220    fn c_file_mints_aggregate_member_tag_at_file_scope() {
27221        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27222        let parsed = parse_cpp_declarations(source, "x.c");
27223        let declarations = parsed.declarations();
27224
27225        assert!(
27226            declarations
27227                .iter()
27228                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27229            "expected a file-scope inner tag, got {declarations:?}"
27230        );
27231        assert!(
27232            declarations
27233                .iter()
27234                .all(|unit| unit.fq_name() != "outer$inner"),
27235            "expected no nested identity, got {declarations:?}"
27236        );
27237        assert!(
27238            declarations
27239                .iter()
27240                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27241        );
27242        // Members still belong to their own aggregate.
27243        assert!(
27244            declarations
27245                .iter()
27246                .any(|unit| unit.fq_name() == "inner.value")
27247        );
27248        assert!(
27249            declarations
27250                .iter()
27251                .any(|unit| unit.fq_name() == "outer.item")
27252        );
27253
27254        let outer = declarations
27255            .iter()
27256            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27257            .expect("outer");
27258        assert!(
27259            parsed
27260                .children
27261                .get(outer)
27262                .into_iter()
27263                .flatten()
27264                .all(|child| child.fq_name() != "inner"),
27265            "the tag must not hang off the aggregate it is written inside: {:?}",
27266            parsed.children
27267        );
27268    }
27269
27270    /// A header carries no compilation language of its own, and a `.cpp`
27271    /// translation unit really does declare a nested class. Both keep exactly
27272    /// the C++ extraction they had before the C dialect existed.
27273    #[test]
27274    #[hardwon(
27275        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27276    )]
27277    fn header_and_cpp_files_keep_nested_tag_identity() {
27278        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27279        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27280            let parsed = parse_cpp_declarations(source, name);
27281            let declarations = parsed.declarations();
27282            assert!(
27283                declarations
27284                    .iter()
27285                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27286                "{name} must keep the nested identity, got {declarations:?}"
27287            );
27288            assert!(
27289                declarations.iter().all(|unit| unit.fq_name() != "inner"),
27290                "{name} must not mint a file-scope tag, got {declarations:?}"
27291            );
27292            assert!(
27293                declarations
27294                    .iter()
27295                    .any(|unit| unit.fq_name() == "outer$inner.value")
27296            );
27297        }
27298    }
27299
27300    #[test]
27301    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27302        let source = r#"
27303template <class K, class V, class Traits = CacheTraits<K>>
27304class ARCCache {
27305 public:
27306    struct ArcSizeInfo {
27307        void BSize() {}
27308    };
27309 private:
27310    struct BMapVal;
27311    struct BListVal;
27312    typedef std::unordered_map<K, BMapVal> BMap;
27313    typedef typename BMap::iterator bmap_iter;
27314    typedef typename std::list<BListVal> BList;
27315    typedef typename BList::iterator blist_iter;
27316    struct BListVal {
27317        bmap_iter map_iter;
27318        BListVal() = default;
27319    };
27320    struct DefinedVal {
27321        DefinedVal() {}
27322    };
27323};
27324"#;
27325        let tree = crate::diagnostics::parse_cpp_tree(source);
27326        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27327        let cpp = crate::adapter::parse_cpp_file_in_dialect(
27328            &file,
27329            source,
27330            &tree,
27331            LanguageDialect::Standard(Language::Cpp),
27332        );
27333        let c =
27334            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27335        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27336            for fq_name in [
27337                "ARCCache$ArcSizeInfo.BSize",
27338                "ARCCache$BListVal.BListVal",
27339                "ARCCache$DefinedVal.DefinedVal",
27340            ] {
27341                assert!(
27342                    parsed
27343                        .declarations()
27344                        .iter()
27345                        .any(|unit| unit.fq_name() == fq_name),
27346                    "{dialect} reading must contain {fq_name}: {:#?}",
27347                    parsed.declarations()
27348                );
27349            }
27350            for flattened in [
27351                "ArcSizeInfo.BSize",
27352                "BListVal.BListVal",
27353                "DefinedVal.DefinedVal",
27354            ] {
27355                assert!(
27356                    parsed
27357                        .declarations()
27358                        .iter()
27359                        .all(|unit| unit.fq_name() != flattened),
27360                    "{dialect} reading must not flatten {flattened}: {:#?}",
27361                    parsed.declarations()
27362                );
27363            }
27364        }
27365    }
27366
27367    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
27368    #[test]
27369    #[hardwon(
27370        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27371    )]
27372    fn uppercase_c_extension_keeps_cpp_tag_scope() {
27373        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27374        let parsed = parse_cpp_declarations(source, "x.C");
27375        assert!(
27376            parsed
27377                .declarations()
27378                .iter()
27379                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27380        );
27381    }
27382
27383    /// There is no such thing as a partially nested tag in C: every level of a
27384    /// nested aggregate chain lands at the same enclosing scope.
27385    #[test]
27386    #[hardwon(
27387        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27388    )]
27389    fn c_file_mints_every_nesting_level_at_file_scope() {
27390        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27391        let parsed = parse_cpp_declarations(source, "z.c");
27392        let declarations = parsed.declarations();
27393
27394        for tag in ["a", "b", "c"] {
27395            assert!(
27396                declarations
27397                    .iter()
27398                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
27399                "expected a file-scope {tag}, got {declarations:?}"
27400            );
27401        }
27402        assert!(
27403            declarations
27404                .iter()
27405                .all(|unit| !unit.fq_name().contains('$')),
27406            "no level may keep a nested identity, got {declarations:?}"
27407        );
27408        // Each member still belongs to the aggregate that declares it.
27409        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27410        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27411        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27412    }
27413
27414    /// An enum tag is a tag; its enumerators stay members of the enum, which is
27415    /// what makes them ordinary identifiers at the enum's own (file) scope.
27416    #[test]
27417    #[hardwon(
27418        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27419    )]
27420    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27421        let source = "struct outer { enum color { RED, GREEN } c; };\n";
27422        let parsed = parse_cpp_declarations(source, "e.c");
27423        let declarations = parsed.declarations();
27424
27425        let color = declarations
27426            .iter()
27427            .find(|unit| unit.is_class() && unit.fq_name() == "color")
27428            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27429        assert!(
27430            declarations
27431                .iter()
27432                .all(|unit| unit.fq_name() != "outer$color")
27433        );
27434        for enumerator in ["color.RED", "color.GREEN"] {
27435            assert!(
27436                declarations.iter().any(|unit| unit.fq_name() == enumerator),
27437                "expected {enumerator}, got {declarations:?}"
27438            );
27439        }
27440        let children = parsed
27441            .children
27442            .get(color)
27443            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27444        assert!(
27445            ["color.RED", "color.GREEN"]
27446                .iter()
27447                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27448            "enumerators must hang off their enum: {children:?}"
27449        );
27450    }
27451
27452    #[test]
27453    #[hardwon(
27454        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27455    )]
27456    fn c_file_mints_member_list_union_at_file_scope() {
27457        let source = "struct outer { union inner { int a; float b; } item; };\n";
27458        let parsed = parse_cpp_declarations(source, "u.c");
27459        let declarations = parsed.declarations();
27460        assert!(
27461            declarations
27462                .iter()
27463                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27464            "expected a file-scope inner union, got {declarations:?}"
27465        );
27466        assert!(
27467            declarations
27468                .iter()
27469                .all(|unit| unit.fq_name() != "outer$inner")
27470        );
27471        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27472        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27473    }
27474
27475    /// A tag declared in a namespace member list is not a file-scope tag: the
27476    /// nearest enclosing non-aggregate scope is the namespace.
27477    #[test]
27478    #[hardwon(
27479        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27480    )]
27481    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27482        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27483        let parsed = parse_cpp_declarations(source, "n.c");
27484        let declarations = parsed.declarations();
27485        let inner = declarations
27486            .iter()
27487            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27488            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27489        assert_eq!(inner.package_name(), "ns");
27490        assert!(
27491            declarations
27492                .iter()
27493                .all(|unit| unit.fq_name() != "ns.outer$inner")
27494        );
27495    }
27496
27497    /// Pins today's treatment of a tag declared inside a function body: the
27498    /// declaration walk does not descend into statement bodies, so no unit is
27499    /// minted for it in either dialect. C block scope is out of scope for the
27500    /// dialect change, and this test proves the change did not disturb it.
27501    #[test]
27502    #[hardwon(
27503        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27504    )]
27505    fn function_local_tags_are_unchanged_in_both_dialects() {
27506        let source =
27507            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
27508        for name in ["y.c", "y.cpp"] {
27509            let parsed = parse_cpp_declarations(source, name);
27510            let declarations = parsed.declarations();
27511            assert!(
27512                declarations
27513                    .iter()
27514                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27515                "{name}: {declarations:?}"
27516            );
27517            for tag in ["localtag", "deeper", "localtag$deeper"] {
27518                assert!(
27519                    declarations.iter().all(|unit| unit.fq_name() != tag),
27520                    "{name} must not mint {tag}, got {declarations:?}"
27521                );
27522            }
27523        }
27524    }
27525
27526    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
27527    /// re-scope: the typedef name is identical in both dialects.
27528    #[test]
27529    #[hardwon(
27530        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27531    )]
27532    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27533        let source = "typedef struct { int v; } T;\n";
27534        for name in ["t.c", "t.cpp"] {
27535            let parsed = parse_cpp_declarations(source, name);
27536            let declarations = parsed.declarations();
27537            assert!(
27538                declarations
27539                    .iter()
27540                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27541                "{name}: {declarations:?}"
27542            );
27543        }
27544    }
27545
27546    /// The same principle for every anonymous aggregate the walk indexes, not
27547    /// just the typedef one: a promoted anonymous union in a member list, an
27548    /// anonymous struct given a receiver type by its declarator, a
27549    /// function-local aggregate, and one inside an inline member function
27550    /// body. The C++ reading used to mint none of these, so a pure C++
27551    /// workspace indexed no members at all (#3301).
27552    #[test]
27553    #[hardwon(
27554        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27555    )]
27556    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27557        let sources = [
27558            "struct outer { union { int a; float b; }; };\n",
27559            "struct outer { struct { int *ops; } sock; };\n",
27560            "void run(void) { struct { int v; } item; }\n",
27561            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27562        ];
27563        for source in sources {
27564            let names = |name: &str| {
27565                parse_cpp_declarations(source, name)
27566                    .declarations()
27567                    .iter()
27568                    .map(|unit| unit.fq_name())
27569                    .collect::<std::collections::BTreeSet<_>>()
27570            };
27571            let c_names = names("a.c");
27572            let cpp_names = names("a.cpp");
27573            assert_eq!(
27574                c_names, cpp_names,
27575                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27576            );
27577            assert!(
27578                c_names
27579                    .iter()
27580                    .any(|name| name.contains('.') && name != "outer"),
27581                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27582            );
27583        }
27584    }
27585
27586    #[test]
27587    #[hardwon(
27588        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27589    )]
27590    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27591        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27592        let parsed = parse_cpp_declarations(source, "socket.c");
27593        let declarations = parsed.declarations();
27594        assert_eq!(
27595            declarations
27596                .iter()
27597                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27598                .count(),
27599            1,
27600            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27601        );
27602        for expected in [
27603            "PAL_HANDLE",
27604            "PAL_HANDLE.sock",
27605            "PAL_HANDLE$sock",
27606            "PAL_HANDLE$sock.ops",
27607        ] {
27608            assert!(
27609                declarations.iter().any(|unit| unit.fq_name() == expected),
27610                "expected {expected}, got {declarations:?}"
27611            );
27612        }
27613    }
27614
27615    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
27616    /// every (node, class-like name) pair a tree offers, and require the same
27617    /// answer from both.
27618    ///
27619    /// The release build has no `debug_assertions` agreement check, so this is
27620    /// what pins the two together there.  Both query orders are exercised:
27621    /// document order is what the walk does, and reverse order proves that a
27622    /// question about an earlier byte than one already answered is still
27623    /// filtered back to its own prefix rather than answered from the wider
27624    /// fold.
27625    ///
27626    /// Returns how many questions were answered with a namespace, so a fixture
27627    /// can assert it actually reached the path (#2754).
27628    fn namespace_forward_scan_agreement(source: &str) -> usize {
27629        let mut parser = tree_sitter::Parser::new();
27630        parser
27631            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27632            .unwrap();
27633        let tree = parser.parse(source, None).unwrap();
27634        let root = tree.root_node();
27635        let ancestry = ParentIndex::new(root);
27636
27637        let mut nodes = Vec::new();
27638        let mut names = std::collections::BTreeSet::new();
27639        let mut cursor = root.walk();
27640        let mut stack = vec![root];
27641        while let Some(node) = stack.pop() {
27642            if matches!(
27643                node.kind(),
27644                "class_specifier" | "struct_specifier" | "union_specifier"
27645            ) && let Some(name) = class_like_name(node, source, &ancestry)
27646            {
27647                names.insert(name);
27648            }
27649            nodes.push(node);
27650            stack.extend(node.named_children(&mut cursor));
27651        }
27652        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
27653        assert!(!names.is_empty(), "fixture declares no class-like name");
27654
27655        let mut answered = 0usize;
27656        for reversed in [false, true] {
27657            let mut scan = CppNamespaceForwardScan::default();
27658            let ordered: Vec<_> = if reversed {
27659                nodes.iter().rev().copied().collect()
27660            } else {
27661                nodes.clone()
27662            };
27663            answered = 0;
27664            for node in ordered {
27665                for name in &names {
27666                    scan.advance_to(root, node.start_byte(), source, &ancestry);
27667                    let carried = scan.unique_earlier_forward(name, node);
27668                    assert_eq!(
27669                        carried,
27670                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
27671                        "carried-forward scan and prefix scan disagree about {name} at \
27672                         {} node starting at byte {} (reversed order: {reversed})",
27673                        node.kind(),
27674                        node.start_byte()
27675                    );
27676                    answered += usize::from(carried.is_some());
27677                }
27678            }
27679        }
27680        answered
27681    }
27682
27683    /// A malformed namespace whose forward declarations are the only identity
27684    /// signal left for the class definitions tree-sitter pushed out to file
27685    /// scope.  Both recovered classes open the guard; only the first one is
27686    /// separated from the namespace by nothing but recovery trivia, so only the
27687    /// first one borrows.  The carried-forward scan has to reproduce both
27688    /// answers.
27689    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
27690namespace ns {
27691class Widget;
27692class Gadget;
27693int x = ;
27694}
27695class API Widget {
27696public:
27697    void first();
27698};
27699class API Gadget {
27700public:
27701    void second();
27702};
27703"#;
27704
27705    #[test]
27706    #[hardwon(
27707        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
27708    )]
27709    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
27710        assert!(
27711            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
27712            "the fixture must actually reach the namespace-borrow path"
27713        );
27714
27715        // Nothing here may be answered, and the two paths have to agree about
27716        // that too: a clean namespace is not an identity proof, two forwards of
27717        // one name are ambiguous rather than a guess, and a forward inside a
27718        // function body is not at namespace scope.
27719        for source in [
27720            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
27721            r#"#define API
27722namespace ns {
27723class Widget;
27724class Widget;
27725int x = ;
27726}
27727class API Widget {
27728public:
27729    void method();
27730};
27731"#,
27732            r#"#define API
27733namespace ns {
27734void host() {
27735    class Widget;
27736}
27737int x = ;
27738}
27739class API Widget {
27740public:
27741    void method();
27742};
27743"#,
27744        ] {
27745            assert_eq!(
27746                namespace_forward_scan_agreement(source),
27747                0,
27748                "no borrow is justified here: {source}"
27749            );
27750        }
27751    }
27752
27753    /// Drive the region reparse and the whitespace-padded reparse it replaced
27754    /// over the same region and require identical trees.
27755    ///
27756    /// The release build has no `debug_assertions` agreement check, so this is
27757    /// what pins the two together there. Each body is reparsed at its own
27758    /// offset and again after a long prefix, because the prefix is the whole
27759    /// difference between the two techniques: the padded parse lexes it as
27760    /// whitespace, the included-range parse never sees it, and the tree has to
27761    /// come out the same either way (#2788).
27762    fn fragmented_class_reparse_agreement(body: &str) {
27763        for prefix in [
27764            String::new(),
27765            "// leading comment\n".to_string(),
27766            // A body starts just after its class head's `{`, which is normally
27767            // mid-line: the padded parse then has spaces before the region on
27768            // the region's own line, and the included-range parse has nothing
27769            // at all before it.
27770            "class Widget : public Base { ".to_string(),
27771            "namespace filler {\n".to_string()
27772                + &"struct Filler { int member; };\n".repeat(200)
27773                + "}\n",
27774            "namespace filler {\n".to_string()
27775                + &"struct Filler { int member; };\n".repeat(200)
27776                + "}\nclass Widget : public Base { ",
27777        ] {
27778            let source = format!("{prefix}{body}");
27779            let start = prefix.len();
27780            let end = source.len();
27781            let region = cpp_reparse_fragmented_class_body(&source, start, end)
27782                .expect("the region reparse must produce a tree");
27783            let padded = cpp_reparse_padded_class_body(&source, start, end)
27784                .expect("the padded reparse must produce a tree");
27785            assert_eq!(
27786                cpp_tree_shape(&region),
27787                cpp_tree_shape(&padded),
27788                "region and padded reparse disagree at offset {start} of {end} bytes"
27789            );
27790            assert_eq!(
27791                region.root_node().start_byte(),
27792                start,
27793                "the reparsed region keeps its original offsets"
27794            );
27795        }
27796    }
27797
27798    #[test]
27799    #[hardwon(
27800        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
27801    )]
27802    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
27803        // A conditional immediately after an access label: the shape the padded
27804        // technique was kept for, because the directive and its macro name
27805        // become an ERROR plus the following declaration's apparent type.
27806        fragmented_class_reparse_agreement(
27807            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
27808        );
27809        fragmented_class_reparse_agreement(
27810            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
27811        );
27812        // The merged inline constructor and the nested fragmented bodies the
27813        // #938 recovery reads out of a reparse.
27814        fragmented_class_reparse_agreement(
27815            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
27816        );
27817        fragmented_class_reparse_agreement(
27818            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
27819        );
27820        // A body that is not member-shaped at all still has to reparse the same
27821        // way, because the admission gate reads the tree to reject it.
27822        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
27823    }
27824
27825    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
27826        let mut siblings = Vec::new();
27827        let mut anchor = node;
27828        while let Some(parent) = anchor.parent() {
27829            let at_translation_unit = parent.kind() == "translation_unit";
27830            let mut sibling = anchor.next_named_sibling();
27831            while let Some(current) = sibling {
27832                if at_translation_unit
27833                    && (current.kind() == "namespace_definition"
27834                        || (current.kind() == "function_definition"
27835                            && first_class_like_child(current).is_some()))
27836                {
27837                    return siblings;
27838                }
27839                siblings.push(current);
27840                if cpp_is_stray_close_brace(current, source) {
27841                    if let Some(semicolon) = current
27842                        .next_named_sibling()
27843                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
27844                    {
27845                        siblings.push(semicolon);
27846                    }
27847                    return siblings;
27848                }
27849                if current.start_byte() >= node.end_byte()
27850                    && matches!(current.kind(), "ERROR" | "labeled_statement")
27851                    && cpp_nested_stray_close_brace(current, source).is_some()
27852                {
27853                    return siblings;
27854                }
27855                sibling = current.next_named_sibling();
27856            }
27857            anchor = parent;
27858        }
27859        siblings
27860    }
27861
27862    #[test]
27863    #[hardwon(
27864        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
27865    )]
27866    fn following_named_siblings_cursor_matches_native_traversal() {
27867        // The damaged #2924 shape scatters members across container siblings,
27868        // and the anonymous punctuation forces the cursor to filter children
27869        // exactly as Node::next_named_sibling does.
27870        let source = r#"
27871namespace api {
27872class PROJECT_API(2, 0) Widget : public virtual Base {
27873  public:
27874    Widget() {}
27875    void first();
27876};
27877enum After { Value };
27878}
27879"#;
27880        let mut parser = tree_sitter::Parser::new();
27881        parser
27882            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27883            .unwrap();
27884        let tree = parser.parse(source, None).unwrap();
27885        let root = tree.root_node();
27886        assert!(root.has_error(), "fixture must remain damaged");
27887
27888        let ancestry = ParentIndex::new(root);
27889        let mut stack = vec![root];
27890        let mut checked = 0;
27891        let mut damaged = 0;
27892        let mut anonymous = 0;
27893        let mut nonempty_sequences = 0;
27894        let mut named_then_anonymous = 0;
27895        while let Some(node) = stack.pop() {
27896            let expected = native_following_named_siblings(node, source)
27897                .iter()
27898                .map(Node::id)
27899                .collect::<Vec<_>>();
27900            let actual = cpp_following_named_siblings(node, source, &ancestry)
27901                .iter()
27902                .map(Node::id)
27903                .collect::<Vec<_>>();
27904            assert_eq!(
27905                actual,
27906                expected,
27907                "cursor traversal diverged at byte {}",
27908                node.start_byte()
27909            );
27910
27911            let mut cursor = node.walk();
27912            let children = node.children(&mut cursor).collect::<Vec<_>>();
27913            stack.extend(children.iter().copied());
27914            checked += 1;
27915            damaged += usize::from(node.is_error() || node.is_missing());
27916            anonymous += usize::from(!node.is_named());
27917            nonempty_sequences += usize::from(!actual.is_empty());
27918            named_then_anonymous += usize::from(
27919                children
27920                    .windows(2)
27921                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
27922            );
27923        }
27924        assert!(checked > 0, "fixture must provide nodes to compare");
27925        assert!(damaged > 0, "fixture must cover damaged nodes");
27926        assert!(anonymous > 0, "fixture must cover anonymous nodes");
27927        assert!(
27928            nonempty_sequences > 0,
27929            "fixture must exercise following named siblings"
27930        );
27931        assert!(
27932            named_then_anonymous > 0,
27933            "fixture must exercise anonymous nodes following a named anchor"
27934        );
27935    }
27936
27937    #[test]
27938    #[hardwon(
27939        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
27940    )]
27941    fn a_replacement_removes_owned_enum_fields() {
27942        let source =
27943            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
27944        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
27945        let mut names: Vec<_> = parsed
27946            .declarations()
27947            .iter()
27948            .map(|unit| unit.fq_name())
27949            .collect();
27950        names.sort();
27951        assert_eq!(
27952            names,
27953            vec![
27954                "Color".to_string(),
27955                "First".to_string(),
27956                "First.A".to_string()
27957            ],
27958            "the replaced Color owns no field any more"
27959        );
27960    }
27961
27962    /// A recovery that re-declares what the file already declared mints
27963    /// nothing.
27964    ///
27965    /// The reparse walk replaces the outer `Widget`, which removes its method,
27966    /// and then re-creates that method from the region. Creation alone would
27967    /// call the method recovered; it was there before the recovery opened, so
27968    /// the recovered set is empty and only the region's own reparse window is
27969    /// recorded (#2787).
27970    #[test]
27971    #[hardwon(
27972        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
27973    )]
27974    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
27975        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
27976                      BEGIN_NS\n\
27977                      namespace demo { struct Widget { void doWork(); }; }\n\
27978                      END_NS\n";
27979        let parsed = parse_cpp_declarations(source, "restored.cpp");
27980        let recovered: Vec<String> = parsed
27981            .materialization_records
27982            .iter()
27983            .filter_map(|record| match record {
27984                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
27985                _ => None,
27986            })
27987            .collect();
27988        assert!(
27989            recovered.is_empty(),
27990            "the region declares nothing the file did not already declare: {recovered:?}"
27991        );
27992        let mut names: Vec<String> = parsed
27993            .declarations()
27994            .iter()
27995            .map(|unit| unit.fq_name())
27996            .collect();
27997        names.sort();
27998        assert_eq!(
27999            names,
28000            vec![
28001                "demo".to_string(),
28002                "demo.Widget".to_string(),
28003                "demo.Widget.doWork".to_string(),
28004            ]
28005        );
28006    }
28007
28008    /// Four macro-sentinel recoveries in one file (#941). Each one must record
28009    /// exactly the declarations it minted -- not the ones an earlier recovery
28010    /// minted, and not the file's other declarations -- in start-byte order,
28011    /// and each record must carry its own reparse window (#2787).
28012    #[test]
28013    #[hardwon(
28014        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28015    )]
28016    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28017        let mut source = String::new();
28018        for index in 0..4 {
28019            let _ = write!(
28020                source,
28021                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
28022            );
28023        }
28024        source.push_str("void outside() {}\n");
28025        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28026
28027        let recovered: Vec<(String, (usize, usize))> = parsed
28028            .materialization_records
28029            .iter()
28030            .filter_map(|record| match record {
28031                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28032                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28033                }
28034                _ => None,
28035            })
28036            .collect();
28037
28038        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28039        for index in 0..4 {
28040            // The window the reparse covers: everything after the opening
28041            // sentinel token up to the newline before the closing one.
28042            let region = format!("namespace demo{index}");
28043            let region_start = source.find(&region).expect("each region is in the source");
28044            let start = source[..region_start]
28045                .rfind("BEGIN_NS")
28046                .expect("each region opens with a sentinel")
28047                + "BEGIN_NS".len();
28048            let end = start
28049                + source[start..]
28050                    .find("END_NS")
28051                    .expect("each region closes with a sentinel")
28052                - 1;
28053            let window = (start, end);
28054            for name in [
28055                format!("demo{index}"),
28056                format!("demo{index}.Widget{index}"),
28057                format!("demo{index}.Widget{index}.doWork{index}"),
28058            ] {
28059                expected.push((name, window));
28060            }
28061        }
28062        assert_eq!(
28063            recovered, expected,
28064            "each recovery records its own minted declarations, in order"
28065        );
28066        assert!(
28067            parsed
28068                .declarations()
28069                .iter()
28070                .any(|unit| unit.fq_name() == "outside"),
28071            "the declaration outside every region stays parsed and unrecovered"
28072        );
28073    }
28074
28075    /// `blocks` copies of one plain class-with-methods template. The class the
28076    /// question is about is always the first one, so the only thing that
28077    /// changes between two of these sources is how much unrelated tree
28078    /// surrounds it.
28079    fn repeated_class_blocks(blocks: usize) -> String {
28080        let mut source = String::from("namespace demo {\n");
28081        for index in 0..blocks {
28082            let _ = write!(
28083                source,
28084                "\nclass Widget{index} {{\npublic:\n    int translate() const {{ return {index}; }}\n    int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n    int field = {index};\n}};\n"
28085            );
28086        }
28087        source.push_str("\n}\n");
28088        source
28089    }
28090
28091    /// #1496: asking whether a recovered class shape claims one range must cost
28092    /// the path to that range, not a pass over the whole translation unit.
28093    ///
28094    /// The C++ inverse scan asks this once per candidate type reference, so a
28095    /// whole-tree walk makes one file's scan quadratic in its own size. Both
28096    /// sources here answer `None` -- these are plain classes that no recovery
28097    /// shape claims -- which is exactly the case that used to pay full price.
28098    #[test]
28099    #[hardwon(
28100        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28101    )]
28102    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28103        let mut answers = Vec::new();
28104        let mut visits = Vec::new();
28105        let mut node_counts = Vec::new();
28106        for blocks in [200usize, 400] {
28107            let source = repeated_class_blocks(blocks);
28108            let mut parser = tree_sitter::Parser::new();
28109            parser
28110                .set_language(&tree_sitter_cpp::LANGUAGE.into())
28111                .unwrap();
28112            let tree = parser.parse(&source, None).unwrap();
28113            let start_byte = source.find("class Widget0 ").expect("first class");
28114            let end_byte = start_byte
28115                + source[start_byte..]
28116                    .find("};")
28117                    .expect("first class terminator")
28118                + "};".len();
28119            let range = Range {
28120                start_byte,
28121                end_byte,
28122                start_line: 0,
28123                end_line: 0,
28124            };
28125            reset_recovered_class_body_node_visits_for_test();
28126            let recovered_export_classes =
28127                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28128            answers.push(recovered_class_body_at(
28129                &recovered_export_classes,
28130                tree.root_node(),
28131                &source,
28132                "Widget0",
28133                &range,
28134            ));
28135            visits.push(recovered_class_body_node_visits_for_test());
28136            let mut nodes = 0usize;
28137            let mut stack = vec![tree.root_node()];
28138            while let Some(node) = stack.pop() {
28139                nodes += 1;
28140                let mut cursor = node.walk();
28141                stack.extend(node.named_children(&mut cursor));
28142            }
28143            node_counts.push(nodes);
28144        }
28145
28146        assert_eq!(
28147            answers,
28148            vec![None, None],
28149            "no recovered shape claims a plain class"
28150        );
28151        assert_eq!(
28152            visits[0], visits[1],
28153            "the walk must follow the range's own path, so doubling the unrelated \
28154             classes must not change the node count: {visits:?} over trees of \
28155             {node_counts:?} nodes"
28156        );
28157        assert!(
28158            visits[1] * 20 < node_counts[1],
28159            "the walk must stay far below one pass over the tree: {visits:?} over \
28160             trees of {node_counts:?} nodes"
28161        );
28162    }
28163
28164    #[test]
28165    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28166    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28167        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28168        let mut parser = tree_sitter::Parser::new();
28169        parser
28170            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28171            .expect("C++ grammar");
28172        let tree = parser.parse(source, None).expect("fixture tree");
28173        let mut stack = vec![tree.root_node()];
28174        let mut recovered = None;
28175        while let Some(node) = stack.pop() {
28176            if node.kind() == "field_declaration"
28177                && let Some(field) = recovered_function_like_field_declarator(node, source)
28178            {
28179                recovered = Some((node, field.name));
28180                break;
28181            }
28182            let mut cursor = node.walk();
28183            stack.extend(node.named_children(&mut cursor));
28184        }
28185        let (declaration, name) = recovered.unwrap_or_else(|| {
28186            panic!(
28187                "pointer-wrapped macro field was not recovered: {}",
28188                tree.root_node().to_sexp()
28189            )
28190        });
28191        assert_eq!(node_text(name, source), "handshake");
28192        let recovered =
28193            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28194        assert_eq!(recovered.pointer_depth(), 1);
28195        assert_eq!(
28196            render_cpp_field_signature(declaration, name, source),
28197            "struct handshake * handshake;"
28198        );
28199    }
28200
28201    #[test]
28202    #[hardwon(
28203        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28204    )]
28205    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28206        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28207        let mut parser = tree_sitter::Parser::new();
28208        parser
28209            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28210            .expect("C++ grammar");
28211        let tree = parser.parse(source, None).expect("fixture tree");
28212        // The macro-declaration grammar reads `PyObject_HEAD` as a
28213        // `macro_modifier`, so the member arrives as an ordinary clean field
28214        // declaration rather than the recovery shape the ERROR-based reader
28215        // exists for.
28216        let mut stack = vec![tree.root_node()];
28217        let mut declaration = None;
28218        while let Some(node) = stack.pop() {
28219            if node.kind() == "field_declaration" {
28220                declaration = Some(node);
28221                break;
28222            }
28223            let mut cursor = node.walk();
28224            stack.extend(node.named_children(&mut cursor));
28225        }
28226        let declaration = declaration.unwrap_or_else(|| {
28227            panic!(
28228                "the pointer field must arrive as an ordinary field declaration: {}",
28229                tree.root_node().to_sexp()
28230            )
28231        });
28232        let declarator = declaration
28233            .child_by_field_name("declarator")
28234            .expect("pointer field declarator");
28235        assert_eq!(node_text(declarator, source), "*image");
28236        assert_eq!(
28237            render_cpp_field_signature(declaration, declarator, source),
28238            "PyObject_HEAD ImagingObject * image;",
28239            "the macro marker is retained as modifier provenance, the same \
28240             treatment an attribute or storage-class specifier gets"
28241        );
28242        assert_eq!(
28243            crate::source_facts::capture_field_type(declaration, "image", source),
28244            (
28245                Some(
28246                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28247                        type_text: "ImagingObject".to_owned(),
28248                        indirection: 1,
28249                        binds_indirectly: true,
28250                        template_arguments: None,
28251                    }
28252                ),
28253                false,
28254            ),
28255            "canonical field facts must use the declared type, not the macro token"
28256        );
28257    }
28258
28259    #[test]
28260    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28261        let source = "class outer { class inner { int v; }; };\n";
28262        let c_parsed = parse_cpp_declarations(source, "k.c");
28263        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28264        let c_declarations = c_parsed.declarations();
28265        let cpp_declarations = cpp_parsed.declarations();
28266        assert!(
28267            c_declarations
28268                .iter()
28269                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28270            "{c_declarations:?}"
28271        );
28272        assert_eq!(
28273            c_declarations
28274                .iter()
28275                .map(|unit| unit.fq_name())
28276                .collect::<std::collections::BTreeSet<_>>(),
28277            cpp_declarations
28278                .iter()
28279                .map(|unit| unit.fq_name())
28280                .collect::<std::collections::BTreeSet<_>>()
28281        );
28282    }
28283    #[test]
28284    fn callable_metadata_records_c_receiver_contracts_structurally() {
28285        const SOURCE: &str = concat!(
28286            "static long accumulate(const long *lines, int count) { return count; }\n",
28287            "\n",
28288            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28289        );
28290        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28291
28292        assert_eq!(
28293            recorded_callable_modifiers(&parsed),
28294            vec![
28295                "accumulate decl_only=false static=false constructor=false \
28296                 visibility=Unknown"
28297                    .to_string(),
28298                "total decl_only=false static=false constructor=false visibility=Unknown"
28299                    .to_string(),
28300            ],
28301        );
28302    }
28303    #[test]
28304    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28305        const SOURCE: &str = concat!(
28306            "namespace shop {\n",
28307            "\n",
28308            "class Client {\n",
28309            "public:\n",
28310            "    Client();\n",
28311            "    void send(int order);\n",
28312            "    static int count();\n",
28313            "protected:\n",
28314            "    virtual int audit(int n) const { return n; }\n",
28315            "private:\n",
28316            "    int tally(int n) { return n; }\n",
28317            "};\n",
28318            "\n",
28319            "struct Ledger {\n",
28320            "    void record(int n) { (void)n; }\n",
28321            "};\n",
28322            "\n",
28323            "Client::Client() {}\n",
28324            "void Client::send(int order) { (void)order; }\n",
28325            "int Client::count() { return 0; }\n",
28326            "\n",
28327            "int total(int order) { return order; }\n",
28328            "\n",
28329            "}\n",
28330        );
28331        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28332
28333        assert_eq!(
28334            recorded_callable_modifiers(&parsed),
28335            vec![
28336                "shop.Client.Client decl_only=false static=false constructor=true \
28337                 visibility=Unknown"
28338                    .to_string(),
28339                "shop.Client.Client decl_only=true static=false constructor=true \
28340                 visibility=Public"
28341                    .to_string(),
28342                "shop.Client.audit decl_only=false static=false constructor=false \
28343                 visibility=Protected"
28344                    .to_string(),
28345                "shop.Client.count decl_only=false static=false constructor=false \
28346                 visibility=Unknown"
28347                    .to_string(),
28348                "shop.Client.count decl_only=true static=true constructor=false \
28349                 visibility=Public"
28350                    .to_string(),
28351                "shop.Client.send decl_only=false static=false constructor=false \
28352                 visibility=Unknown"
28353                    .to_string(),
28354                "shop.Client.send decl_only=true static=false constructor=false \
28355                 visibility=Public"
28356                    .to_string(),
28357                "shop.Client.tally decl_only=false static=false constructor=false \
28358                 visibility=Private"
28359                    .to_string(),
28360                "shop.Ledger.record decl_only=false static=false constructor=false \
28361                 visibility=Public"
28362                    .to_string(),
28363                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28364                    .to_string(),
28365            ],
28366        );
28367    }
28368
28369    #[test]
28370    fn reparsed_member_signature_grammar_contract() {
28371        use crate::grammar_test_support::{nodes, tree};
28372
28373        // These are class-member fragments reparsed at translation-unit scope,
28374        // exactly the boundary the recovery adapter receives from the grammar.
28375        // Identifier-only annotation errors retain a complete declaration;
28376        // literal payloads and missing signature tokens cannot be admitted.
28377        for (source, expected, error_children, missing_close) in [
28378            ("int f(int n) const { return n; }", true, vec![], false),
28379            (
28380                "int f(int n) const nonneg { return n; }",
28381                true,
28382                vec!["identifier"],
28383                false,
28384            ),
28385            (
28386                "int f(int n) ATTR 42 { return n; }",
28387                false,
28388                vec!["identifier", "number_literal"],
28389                false,
28390            ),
28391            (
28392                "int f(int n = 42 43) { return n; }",
28393                false,
28394                vec!["number_literal"],
28395                false,
28396            ),
28397            ("int f(int n { return n; }", false, vec![], true),
28398        ] {
28399            let parsed = tree(source);
28400            let root = parsed.root_node();
28401            let function = root.named_child(0).unwrap();
28402            assert_eq!(
28403                function.kind(),
28404                "function_definition",
28405                "{source}: {}",
28406                root.to_sexp()
28407            );
28408            let declarator = function.child_by_field_name("declarator").unwrap();
28409            assert_eq!(declarator.kind(), "function_declarator");
28410            let parameters = declarator.child_by_field_name("parameters").unwrap();
28411            assert_eq!(parameters.kind(), "parameter_list");
28412            assert_eq!(
28413                function.child_by_field_name("body").unwrap().kind(),
28414                "compound_statement"
28415            );
28416            let all_nodes = nodes(function);
28417            let errors: Vec<_> = all_nodes
28418                .iter()
28419                .filter(|node| node.kind() == "ERROR")
28420                .collect();
28421            let kinds: Vec<_> = errors
28422                .iter()
28423                .flat_map(|error| {
28424                    error
28425                        .named_children(&mut error.walk())
28426                        .map(|child| child.kind())
28427                        .collect::<Vec<_>>()
28428                })
28429                .collect();
28430            assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28431            assert_eq!(
28432                all_nodes
28433                    .iter()
28434                    .any(|node| node.is_missing() && node.kind() == ")"),
28435                missing_close,
28436                "{source}"
28437            );
28438            assert_eq!(
28439                cpp_reparsed_members_are_indexable(root, source),
28440                expected,
28441                "{source}: {}",
28442                root.to_sexp()
28443            );
28444        }
28445    }
28446    #[test]
28447    fn qualified_and_template_declarator_name_grammar_contract() {
28448        use crate::grammar_test_support::{nodes, tree};
28449        let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28450        let parsed = tree(source);
28451        let all = nodes(parsed.root_node());
28452        for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28453            let qualified = all
28454                .iter()
28455                .copied()
28456                .find(|node| {
28457                    node.kind() == "qualified_identifier" && node_text(*node, source) == written
28458                })
28459                .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28460            let scope = qualified.child_by_field_name("scope").unwrap();
28461            assert_eq!(node_text(scope, source), owner);
28462            assert!(qualified.child_by_field_name("name").is_some());
28463            assert_eq!(extract_declarator_name(qualified, source), written);
28464        }
28465        let function = all
28466            .iter()
28467            .copied()
28468            .find(|node| {
28469                node.kind() == "function_declarator"
28470                    && node
28471                        .child_by_field_name("declarator")
28472                        .is_some_and(|name| name.kind() == "template_function")
28473            })
28474            .unwrap_or_else(|| {
28475                panic!(
28476                    "missing template declarator: {}",
28477                    parsed.root_node().to_sexp()
28478                )
28479            });
28480        let template = function.child_by_field_name("declarator").unwrap();
28481        let name = template.child_by_field_name("name").unwrap();
28482        assert_eq!(name.kind(), "identifier");
28483        assert_eq!(node_text(name, source), "make");
28484        assert_eq!(
28485            template.child_by_field_name("arguments").unwrap().kind(),
28486            "template_argument_list"
28487        );
28488        assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28489    }
28490
28491    fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28492        let mut declarations = file
28493            .declarations()
28494            .iter()
28495            .map(|unit| {
28496                let callable = unit.is_function().then(|| {
28497                    let mut labels = file
28498                        .signature_metadata
28499                        .get(unit)
28500                        .into_iter()
28501                        .flatten()
28502                        .map(|metadata| {
28503                            metadata
28504                                .parameters()
28505                                .iter()
28506                                .map(|parameter| parameter.label())
28507                                .collect::<Vec<_>>()
28508                        })
28509                        .collect::<Vec<_>>();
28510                    labels.sort();
28511                    (unit.signature(), labels)
28512                });
28513                let fields = unit.is_field().then(|| {
28514                    let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28515                    signatures.sort();
28516                    signatures.dedup();
28517                    signatures
28518                });
28519                format!(
28520                    "{:?} {} {fields:?} {callable:?}",
28521                    unit.kind(),
28522                    unit.fq_name()
28523                )
28524            })
28525            .collect::<Vec<_>>();
28526        declarations.sort();
28527        declarations
28528    }
28529
28530    /// Metamorphic oracle: erasing declaration decorators, expanding field-list
28531    /// macros, and spelling a namespace explicitly preserve declaration identity.
28532    /// Compare the complete inventory so a recovered initializer or macro token
28533    /// cannot pass merely because the intended callable was also recovered.
28534    #[test]
28535    fn recovery_declarations_agree_with_explicit_expansions() {
28536        let cases = [
28537            (
28538                "decorated constructors",
28539                "class SIMD_4x26 final { public:\n explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}\n BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}\n int m_v; };\n",
28540                "class SIMD_4x26 final { public:\n explicit SIMD_4x26(int v) : m_v(v) {}\n SIMD_4x26() : m_v(0) {}\n int m_v; };\n",
28541            ),
28542            (
28543                "namespace sentinel",
28544                "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28545                "namespace demo { class Widget { public: int first; void method(); }; }\n",
28546            ),
28547            (
28548                "wrapped prototypes",
28549                "LIBRARY_DEPRECATED(\n LIBRARY_API struct library_context * library_init_from_file(const char * path_model),\n \"use library_init_from_file_with_params instead\"\n);\nLIBRARY_API int next_function(int value);\n",
28550                "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28551            ),
28552            (
28553                "namespace forward across directives",
28554                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28555                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28556            ),
28557            (
28558                "local forward does not lend namespace",
28559                "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28560                "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28561            ),
28562            (
28563                "object declarator does not lend namespace",
28564                "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28565                "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28566            ),
28567            (
28568                "const template return",
28569                r###"template<class T> struct C { API const T f() { return {}; } };"###,
28570                r###"template<class T> struct C { const T f() { return {}; } };"###,
28571            ),
28572            (
28573                "collapsed struct and union",
28574                r###"#define FIRST int first;
28575#define SECOND int first; int second;
28576union One { FIRST SECOND
28577};
28578struct Two { FIRST SECOND
28579};
28580struct Tail { UNDEFINED
28581};
28582"###,
28583                r###"#define FIRST int first;
28584#define SECOND int first; int second;
28585union One { int first; int first; int second;
28586};
28587struct Two { int first; int first; int second;
28588};
28589struct Tail { UNDEFINED
28590};
28591"###,
28592            ),
28593            (
28594                "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28595                r###"
28596#pragma once
28597#define DEMO_DEPRECATED(message)
28598namespace demo {
28599struct Base {
28600    static int aligned(int value) { return value; }
28601    int legacy(int value) const
28602        DEMO_DEPRECATED("use replacement()") { return value; }
28603    int replacement() const;
28604    void run(int value);
28605};
28606struct OtherBase {
28607    void run(int value);
28608    static int aligned(int value) { return value; }
28609};
28610struct Derived : Base {};
28611struct Override : Base {
28612    void run(int value);
28613    static int aligned(int value) { return value; }
28614};
28615struct RecoveredOverride : Base {
28616    int legacy(int value) const
28617        DEMO_DEPRECATED("use replacement()") { return value; }
28618    void run(int value);
28619};
28620struct Hidden : Base {
28621    void run(int first, int second);
28622    static int aligned(int first, int second) { return first + second; }
28623};
28624struct Ambiguous : Base, OtherBase {};
28625}
28626struct Global {};
28627"###,
28628                r###"
28629#pragma once
28630#define DEMO_DEPRECATED(message)
28631namespace demo {
28632struct Base {
28633    static int aligned(int value) { return value; }
28634    int legacy(int value) const
28635         { return value; }
28636    int replacement() const;
28637    void run(int value);
28638};
28639struct OtherBase {
28640    void run(int value);
28641    static int aligned(int value) { return value; }
28642};
28643struct Derived : Base {};
28644struct Override : Base {
28645    void run(int value);
28646    static int aligned(int value) { return value; }
28647};
28648struct RecoveredOverride : Base {
28649    int legacy(int value) const
28650         { return value; }
28651    void run(int value);
28652};
28653struct Hidden : Base {
28654    void run(int first, int second);
28655    static int aligned(int first, int second) { return first + second; }
28656};
28657struct Ambiguous : Base, OtherBase {};
28658}
28659struct Global {};
28660"###,
28661            ),
28662            (
28663                "sentinel union",
28664                r###"BEGIN_NS
28665union Storage {int value; char byte;};
28666END_NS
28667"###,
28668                r###"union Storage {int value; char byte;};
28669"###,
28670            ),
28671            (
28672                "compact sentinel class",
28673                r###"BEGIN_NS
28674class Widget {int first; void method();};
28675END_NS
28676"###,
28677                r###"class Widget {int first; void method();};
28678"###,
28679            ),
28680            (
28681                "three decorators",
28682                "class C { FIRST SECOND THIRD void f() {} };",
28683                "class C { void f() {} };",
28684            ),
28685            (
28686                "overlapping field lists",
28687                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
28688                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
28689            ),
28690            (
28691                "identical redefinition",
28692                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
28693                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
28694            ),
28695            (
28696                "unconditional reset",
28697                "#if MODE\n#define FIELDS int left;\n#else\n#define FIELDS int right;\n#endif\n#undef FIELDS\n#define FIELDS int restored;\nstruct Handle { FIELDS int tail;\n};\n",
28698                "#if MODE\n#define FIELDS int left;\n#else\n#define FIELDS int right;\n#endif\n#undef FIELDS\n#define FIELDS int restored;\nstruct Handle { int restored; int tail;\n};\n",
28699            ),
28700        ];
28701        for (label, source_lf, expanded_lf) in cases {
28702            for (source, expanded) in [
28703                (source_lf.to_owned(), expanded_lf.to_owned()),
28704                (
28705                    source_lf.replace('\n', "\r\n"),
28706                    expanded_lf.replace('\n', "\r\n"),
28707                ),
28708            ] {
28709                let tree = crate::grammar_test_support::tree(&source);
28710                let nodes = crate::grammar_test_support::nodes(tree.root_node());
28711                match label {
28712                    "decorated constructors" => assert!(
28713                        nodes.iter().any(|node| {
28714                            node.kind() == "ERROR"
28715                                && node
28716                                    .named_child(0)
28717                                    .is_some_and(|head| head.kind() == "macro_modifier_declarator")
28718                        }),
28719                        "{}",
28720                        tree.root_node().to_sexp()
28721                    ),
28722                    "wrapped prototypes" => assert!(
28723                        tree.root_node().named_child(0).is_some_and(|node| {
28724                            node.kind() == "ERROR"
28725                                && node
28726                                    .named_child(0)
28727                                    .is_some_and(|head| head.kind() == "function_declarator")
28728                        }),
28729                        "{}",
28730                        tree.root_node().to_sexp()
28731                    ),
28732                    "collapsed struct and union" => assert!(
28733                        nodes.iter().any(|node| {
28734                            node.kind() == "union"
28735                                && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
28736                        }),
28737                        "{}",
28738                        tree.root_node().to_sexp()
28739                    ),
28740                    "object declarator does not lend namespace" => assert!(
28741                        nodes
28742                            .iter()
28743                            .any(|node| { node.kind() == "parenthesized_declarator" }),
28744                        "{}",
28745                        tree.root_node().to_sexp()
28746                    ),
28747                    _ => {}
28748                }
28749                let parsed = parse_cpp_declarations(&source, "expansion.h");
28750                let explicit = parse_cpp_declarations(&expanded, "expansion.h");
28751                assert_eq!(
28752                    declaration_expansion_inventory(&parsed),
28753                    declaration_expansion_inventory(&explicit),
28754                    "{label}"
28755                );
28756                for unit in parsed.declarations() {
28757                    for range in parsed.declaration_ranges(unit) {
28758                        assert!(
28759                            range.start_byte < range.end_byte,
28760                            "{label}: {unit:?}: {range:?}"
28761                        );
28762                        for (offset, line) in [
28763                            (range.start_byte, range.start_line),
28764                            (range.end_byte, range.end_line),
28765                        ] {
28766                            assert_eq!(
28767                                line,
28768                                source[..offset]
28769                                    .bytes()
28770                                    .filter(|byte| *byte == b'\n')
28771                                    .count()
28772                                    + 1,
28773                                "{label}: {unit:?}: {range:?}"
28774                            );
28775                        }
28776                    }
28777                }
28778            }
28779        }
28780    }
28781
28782    #[test]
28783    #[hardwon(
28784        "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
28785    )]
28786    fn recovered_class_constructor_agrees_with_explicit_expansion() {
28787        let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
28788        let tree = crate::grammar_test_support::tree(source);
28789        assert!(
28790            crate::grammar_test_support::nodes(tree.root_node())
28791                .iter()
28792                .any(|node| node.kind() == "call_expression"
28793                    && node
28794                        .child_by_field_name("arguments")
28795                        .is_some_and(|arguments| arguments
28796                            .named_child(0)
28797                            .is_some_and(|argument| argument.kind() == "binary_expression"))),
28798            "{}",
28799            tree.root_node().to_sexp()
28800        );
28801        let actual = parse_cpp_declarations(source, "constructor.h");
28802        let explicit = parse_cpp_declarations(
28803            "class value { public: value(pn_data_t* d); int following(); };",
28804            "constructor.h",
28805        );
28806        assert_eq!(function_identities(&actual), function_identities(&explicit));
28807    }
28808
28809    #[test]
28810    #[hardwon(
28811        "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
28812    )]
28813    fn recovered_base_initializer_is_not_a_declaration() {
28814        let source = r###"
28815namespace api {
28816
28817/**
28818* Doc comment
28819*/
28820class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
28821   public:
28822      /**
28823      * Construct from a point.
28824      */
28825      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
28826
28827#if defined(PROJECT_HAS_LEGACY_POINT)
28828      /**
28829      * Construct from a legacy point.
28830      */
28831      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
28832#endif
28833
28834      std::string algo_name() const override;
28835
28836      AlgorithmIdentifier algorithm_identifier() const override;
28837};
28838
28839}
28840"###;
28841        let tree = crate::grammar_test_support::tree(source);
28842        assert!(
28843            tree.root_node().has_error(),
28844            "{}",
28845            tree.root_node().to_sexp()
28846        );
28847        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
28848        let actual = parse_cpp_declarations(source, "virtual-base.h");
28849        let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
28850        assert_eq!(
28851            declaration_expansion_inventory(&actual),
28852            declaration_expansion_inventory(&explicit)
28853        );
28854    }
28855
28856    #[test]
28857    #[ignore = "finds real bug (#6902): semicolon-free macro envelope hides records and restore"]
28858    fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
28859        let source = r###"struct Aggregate;
28860
28861#define NOARG
28862LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
28863#undef NOARG
28864
28865struct Context { int value; };
28866struct Visit { int value; };
28867
28868LIST_DECLS(static, deps, struct Aggregate *, unused)
28869LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
28870
28871static void target(struct Context *ctx, int first, int second, struct Visit *visit);
28872
28873static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
28874static struct Context *restore(struct Aggregate *value) {
28875  return (struct Context *) value;
28876}
28877
28878static void caller(struct Context *ctx, struct Visit *visit) {
28879  target(ctx, 0, 0, visit); /* recovered prototype */
28880}
28881
28882static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
28883  (void) ctx;
28884  (void) first;
28885  (void) second;
28886  (void) visit;
28887}
28888"###;
28889        let expanded = r###"struct Aggregate;
28890
28891#define NOARG
28892#undef NOARG
28893
28894struct Context { int value; };
28895struct Visit { int value; };
28896
28897
28898static void target(struct Context *ctx, int first, int second, struct Visit *visit);
28899
28900static struct Context *restore(struct Aggregate *value);
28901static struct Context *restore(struct Aggregate *value) {
28902  return (struct Context *) value;
28903}
28904
28905static void caller(struct Context *ctx, struct Visit *visit) {
28906  target(ctx, 0, 0, visit); /* recovered prototype */
28907}
28908
28909static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
28910  (void) ctx;
28911  (void) first;
28912  (void) second;
28913  (void) visit;
28914}
28915"###;
28916        let tree = crate::grammar_test_support::tree(source);
28917        assert!(
28918            tree.root_node().has_error(),
28919            "{}",
28920            tree.root_node().to_sexp()
28921        );
28922        let actual = parse_cpp_declarations(source, "expansion.h");
28923        let explicit = parse_cpp_declarations(expanded, "expansion.h");
28924        assert_eq!(
28925            declaration_expansion_inventory(&actual),
28926            declaration_expansion_inventory(&explicit)
28927        );
28928    }
28929
28930    #[test]
28931    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
28932    fn recovered_sentinel_alias_agrees_with_expansion() {
28933        let source = r###"namespace absl {
28934ABSL_NAMESPACE_BEGIN namespace container_internal {
28935
28936class raw_hash_set : public Base {
28937 public:
28938  using value_type = int;
28939
28940  template <class U,
28941            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
28942  void insert(U value) { (void)value; }
28943
28944  struct InsertSlot {
28945    raw_hash_set& s;
28946  };
28947};
28948
28949}
28950ABSL_NAMESPACE_END
28951}"###;
28952        let expanded = r###"namespace absl {
28953namespace container_internal {
28954
28955class raw_hash_set : public Base {
28956 public:
28957  using value_type = int;
28958
28959  template <class U>
28960  void insert(U value) { (void)value; }
28961
28962  struct InsertSlot {
28963    raw_hash_set& s;
28964  };
28965};
28966
28967}
28968
28969}"###;
28970        let tree = crate::grammar_test_support::tree(source);
28971        assert!(
28972            tree.root_node().has_error(),
28973            "{}",
28974            tree.root_node().to_sexp()
28975        );
28976        let actual = parse_cpp_declarations(source, "expansion.h");
28977        let explicit = parse_cpp_declarations(expanded, "expansion.h");
28978        assert_eq!(
28979            declaration_expansion_inventory(&actual),
28980            declaration_expansion_inventory(&explicit)
28981        );
28982    }
28983
28984    /// The structured alias facts the resolver and navigation read, for one
28985    /// declaration of `fq_name`.
28986    fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
28987        let units = file
28988            .declarations()
28989            .iter()
28990            .filter(|unit| unit.fq_name() == fq_name)
28991            .collect::<Vec<_>>();
28992        let [unit] = units.as_slice() else {
28993            panic!("expected one {fq_name}: {:#?}", file.declarations());
28994        };
28995        let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
28996        let fact = file
28997            .source_declaration_units
28998            .iter()
28999            .filter(|(_, linked)| linked == *unit)
29000            .map(|(declaration, _)| {
29001                facts
29002                    .declarations
29003                    .iter()
29004                    .find(|fact| fact.declaration == *declaration)
29005                    .expect("alias source fact")
29006            })
29007            .map(|fact| {
29008                (
29009                    &fact.alias_target,
29010                    &fact.alias_target_text,
29011                    &fact.lexical_path,
29012                    &fact.guard_requirements,
29013                    &fact.file_scope_alias,
29014                )
29015            })
29016            .collect::<Vec<_>>();
29017        let parents = file
29018            .children
29019            .iter()
29020            .filter(|(_, children)| children.contains(unit))
29021            .map(|(parent, _)| parent.fq_name())
29022            .collect::<Vec<_>>();
29023        format!(
29024            "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29025            unit.kind(),
29026            file.type_aliases.contains(unit),
29027            file.signatures.get(unit),
29028            file.signature_metadata.get(unit),
29029            file.cpp_template_metadata.get(unit),
29030        )
29031    }
29032
29033    #[test]
29034    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29035    fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29036        let source = r###"namespace absl {
29037ABSL_NAMESPACE_BEGIN namespace container_internal {
29038
29039class raw_hash_set : public Base {
29040 public:
29041  using value_type = int;
29042  template <class T>
29043  using rebind = Base<T>;
29044  using key_type = value_type;
29045#ifdef ABSL_HAVE_SLOT
29046  using slot_type = long;
29047#endif
29048
29049  template <class U,
29050            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29051  void insert(U value) { (void)value; }
29052
29053  struct InsertSlot {
29054    raw_hash_set& s;
29055  };
29056};
29057
29058}
29059ABSL_NAMESPACE_END
29060}"###;
29061        let expanded = source
29062            .replace("ABSL_NAMESPACE_BEGIN ", "")
29063            .replace("ABSL_NAMESPACE_END", "")
29064            .replace(
29065                ",\n            REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29066                ">",
29067            );
29068        assert!(
29069            crate::grammar_test_support::tree(source)
29070                .root_node()
29071                .has_error()
29072        );
29073        assert!(
29074            !crate::grammar_test_support::tree(&expanded)
29075                .root_node()
29076                .has_error()
29077        );
29078        let actual = parse_cpp_declarations(source, "expansion.h");
29079        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29080        assert_eq!(
29081            declaration_expansion_inventory(&actual),
29082            declaration_expansion_inventory(&explicit)
29083        );
29084        for (alias, text) in [
29085            ("value_type", "using value_type = int;"),
29086            ("rebind", "using rebind = Base<T>;"),
29087            ("key_type", "using key_type = value_type;"),
29088            ("slot_type", "using slot_type = long;"),
29089        ] {
29090            let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29091            // Kind, owner, target, template head, guard, and lexical scope all
29092            // agree with the explicitly written class.
29093            assert_eq!(
29094                sentinel_alias_facts(&actual, &fq_name),
29095                sentinel_alias_facts(&explicit, &fq_name),
29096                "{alias}"
29097            );
29098            // The range is the alias's own source text, line included.
29099            let unit = actual
29100                .declarations()
29101                .iter()
29102                .find(|unit| unit.fq_name() == fq_name)
29103                .unwrap();
29104            let start = source.find(text).unwrap();
29105            assert_eq!(
29106                actual.declaration_ranges(unit),
29107                [Range {
29108                    start_byte: start,
29109                    end_byte: start + text.len(),
29110                    start_line: source[..start].matches('\n').count() + 1,
29111                    end_line: source[..start].matches('\n').count() + 1,
29112                }],
29113                "{alias}"
29114            );
29115        }
29116        let rendered =
29117            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29118        assert!(
29119            rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29120                && rendered
29121                    .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29122            "the target resolves in the recovered class: {rendered}"
29123        );
29124        assert!(
29125            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29126                .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29127            "the guarded alias keeps its guard"
29128        );
29129    }
29130
29131    #[test]
29132    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29133    fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29134        // Without the class's displaced `};` the sentinel recovery cannot
29135        // prove where raw_hash_set ends, so it recovers no class at all.
29136        let source = r###"namespace absl {
29137ABSL_NAMESPACE_BEGIN namespace container_internal {
29138
29139class raw_hash_set : public Base {
29140 public:
29141  using value_type = int;
29142
29143  template <class U,
29144            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29145  void insert(U value) { (void)value; }
29146
29147}
29148ABSL_NAMESPACE_END
29149}"###;
29150        let parsed = parse_cpp_declarations(source, "unproven.h");
29151        let declarations = parsed.declarations();
29152        assert!(
29153            declarations
29154                .iter()
29155                .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29156            "no member may claim an owner the recovery did not prove: {declarations:#?}"
29157        );
29158        assert!(
29159            declarations
29160                .iter()
29161                .filter(|unit| unit.identifier() == "value_type")
29162                .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29163            "the alias must not become a field: {declarations:#?}"
29164        );
29165    }
29166
29167    #[test]
29168    #[ignore = "finds real bug (#6902): consecutive decorated classes lose most members"]
29169    fn recovered_consecutive_export_classes_agree_with_expansion() {
29170        let source =
29171            function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29172        let tree = crate::grammar_test_support::tree(&source);
29173        assert!(
29174            tree.root_node().has_error(),
29175            "{}",
29176            tree.root_node().to_sexp()
29177        );
29178        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29179        let actual = parse_cpp_declarations(&source, "expansion.h");
29180        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29181        assert_eq!(
29182            declaration_expansion_inventory(&actual),
29183            declaration_expansion_inventory(&explicit)
29184        );
29185    }
29186
29187    #[test]
29188    #[hardwon(
29189        "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29190    )]
29191    fn standalone_field_list_agrees_with_expansion() {
29192        let source =
29193            "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29194        let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29195        let tree = crate::grammar_test_support::tree(source);
29196        assert!(
29197            crate::grammar_test_support::nodes(tree.root_node())
29198                .iter()
29199                .any(|node| node.kind() == "ERROR"
29200                    && node
29201                        .named_child(0)
29202                        .is_some_and(|child| child.kind() == "identifier")),
29203            "{}",
29204            tree.root_node().to_sexp()
29205        );
29206        let actual = parse_cpp_declarations(source, "expansion.h");
29207        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29208        assert_eq!(
29209            declaration_expansion_inventory(&actual),
29210            declaration_expansion_inventory(&explicit)
29211        );
29212    }
29213
29214    #[test]
29215    #[hardwon(
29216        "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29217    )]
29218    fn recovered_constructor_parameters_are_not_fields() {
29219        let source = r#"namespace Botan {
29220class EXPORT(2,0) Key final {
29221public:
29222    Key(int value);
29223    void run();
29224};
29225class EXPORT(2,0) DL_Group final {
29226public:
29227    ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29228    DL_Group(int value);
29229    int get_p() const;
29230};
29231}"#;
29232        let actual = parse_cpp_declarations(source, "constructors.h");
29233        let expanded = source
29234            .replace("EXPORT(2,0) ", "")
29235            .replace("ATTRIBUTE(\"use factory\") ", "");
29236        let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29237        assert_eq!(
29238            declaration_expansion_inventory(&actual),
29239            declaration_expansion_inventory(&explicit)
29240        );
29241    }
29242}