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;
10use brokk_bifrost_core::analyzer::common::{
11    node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
12};
13pub use brokk_bifrost_core::analyzer::cpp_facts::{
14    CppBaseSpecifierFact, CppComparableNode, CppComparableParameter, CppComparableSlot,
15};
16use brokk_bifrost_core::analyzer::fq_name::{
17    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
18};
19use brokk_bifrost_core::analyzer::model::LanguageDialect;
20use brokk_bifrost_core::analyzer::model::{
21    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
22    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
23    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
24    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
25    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
26};
27use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
28use brokk_bifrost_core::analyzer::source_facts::{
29    PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
30    SourceOccurrenceProvenance,
31};
32use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
33use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
34use brokk_bifrost_core::analyzer::structural::materialization::{
35    GenerationKind, MaterializationRecord,
36};
37use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
38use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
39use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
40use brokk_bifrost_core::analyzer::tree_walk::{
41    NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
42    push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
43};
44use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
45use brokk_bifrost_core::hash::{HashMap, HashSet};
46use regex::Regex;
47use std::cell::RefCell;
48#[cfg(any(debug_assertions, test))]
49use tree_sitter::Parser;
50use tree_sitter::{Node, Tree};
51
52/// Intern one qualified-name segment in the process-global interner.
53fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
54    segment_interner().intern(text, kind)
55}
56
57/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
58/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
59/// `::` head is exactly the mixed-separator store issue #1163 is about; the
60/// structured form records each namespace component, and the equivalence check
61/// renders it natively (with `::` between adjacent Package segments) so it
62/// round-trips to the legacy string. Splitting the already-joined string here is
63/// the M1 bridge — the legacy strings stay authoritative until M3.
64fn cpp_push_package(fq: &mut FqName, package_name: &str) {
65    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
66        fq.push(cpp_segment(component, SegmentKind::Package));
67    }
68}
69
70/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
71const CPP_PACKAGE_SEPARATOR: &str = "::";
72
73/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
74/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
75/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
76/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
77/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
78/// cpp-specific native rendering rule is needed for this chain.
79fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
80    let mut first = true;
81    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
82    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
83    // entry point, not re-inference of an already-structured name.
84    for component in chain.split('$').filter(|c| !c.is_empty()) {
85        let kind = if first {
86            SegmentKind::Type
87        } else {
88            SegmentKind::Nested
89        };
90        fq.push(cpp_segment(component, kind));
91        first = false;
92    }
93}
94
95/// Structured name for a C++ namespace module: every `::`-separated component is
96/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
97/// `short_name` with an empty `package_name`).
98fn cpp_namespace_fq(full_name: &str) -> FqName {
99    let mut fq = FqName::new();
100    cpp_push_package(&mut fq, full_name);
101    fq
102}
103
104/// The per-level namespace components a `namespace_definition`'s `name` field
105/// declares.
106///
107/// A C++17 nested definition (`namespace a::b::c`) parses as a
108/// `nested_namespace_specifier` whose named children are the per-level
109/// `namespace_identifier`s plus, for three or more levels, a further
110/// `nested_namespace_specifier`; the `::` separators, the optional per-level
111/// `inline`, and the leading global `::` are all anonymous tokens the walk
112/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
113/// segment path as the expanded `namespace a { namespace b { } }` form.
114///
115/// A shape outside that grammar is the deliberately ill-formed source the
116/// diagnostic corpora carry. Those keep their historical single-component
117/// reading of the raw name text, which the caller still joins to the lexical
118/// namespace exactly as before.
119fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
120    let mut components = Vec::new();
121    let mut stack = vec![node];
122    while let Some(current) = stack.pop() {
123        match current.kind() {
124            "namespace_identifier" | "identifier" => {
125                components.push(normalize_cpp_whitespace(node_text(current, source)));
126            }
127            "nested_namespace_specifier" => {
128                for index in (0..current.named_child_count()).rev() {
129                    stack.push(
130                        current
131                            .named_child(index)
132                            .expect("index below the node's own named child count"),
133                    );
134                }
135            }
136            _ => return cpp_raw_namespace_name_components(node, source),
137        }
138    }
139    if components.iter().any(String::is_empty) {
140        return cpp_raw_namespace_name_components(node, source);
141    }
142    components
143}
144
145/// The historical reading of a namespace name node: its whole source text as
146/// one component, with a leading global `::` marker dropped so the caller's
147/// global-scope handling stays the AST boundary rather than a text prefix.
148///
149/// This is the recovery path for source outside the C++ grammar, so the text it
150/// returns can carry a separator that names nothing. react-native-windows
151/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
152/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
153/// trailing separator with no tail. The caller stores this component in
154/// `short_name` and derives the fq by splitting it back apart, so an empty tail
155/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
156/// here, at the one place the malformed text enters, rather than leaving each
157/// consumer to guard (#2353, a variant of #1878).
158fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
159    let start = node
160        .child(0)
161        .filter(|child| !child.is_named() && child.kind() == "::")
162        .map_or(node.start_byte(), |marker| marker.end_byte());
163    let text = normalize_cpp_whitespace(
164        source
165            .get(start..node.end_byte())
166            .expect("namespace name node covers one source range"),
167    );
168    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
169    if text.is_empty() {
170        return Vec::new();
171    }
172    vec![text]
173}
174
175/// Return the named namespace path that structurally encloses `node`.
176///
177/// This intentionally follows namespace AST ancestors rather than inspecting
178/// source text. Anonymous namespaces are not representable in the legacy C++
179/// package field, so a path containing one fails closed.
180fn cpp_lexical_namespace_name<'tree>(
181    node: Node<'tree>,
182    source: &str,
183    ancestry: &ParentIndex<'tree>,
184) -> Option<String> {
185    let mut components = Vec::new();
186    let mut ancestor = ancestry.parent(node);
187    while let Some(current) = ancestor {
188        if current.kind() == "namespace_definition" {
189            let name_node = current.child_by_field_name("name")?;
190            let name = normalize_cpp_whitespace(node_text(name_node, source));
191            if name.is_empty() {
192                return None;
193            }
194            components.push(name);
195        }
196        ancestor = ancestry.parent(current);
197    }
198    if components.is_empty() {
199        return None;
200    }
201    components.reverse();
202    // Same shared empty-component decision as `cpp_push_package`, which splits
203    // this string back apart: an enclosing namespace whose own name node is
204    // malformed contributes a component carrying its own separator (#2353).
205    Some(
206        normalize_joined(
207            &components.join(CPP_PACKAGE_SEPARATOR),
208            CPP_PACKAGE_SEPARATOR,
209        )
210        .into_owned(),
211    )
212}
213
214/// Nested-class `$` join for short names. An anonymous parent class (empty
215/// short_name) contributes no segment: the FqName bridge drops empty
216/// components, so a bare `parent$child` join would desync `short_name` from
217/// the fq and trip the package/short boundary assert in
218/// `CodeUnit::with_signature_and_fq` (#2140).
219fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
220    if parent_short.is_empty() {
221        name.to_string()
222    } else {
223        format!("{parent_short}${name}")
224    }
225}
226
227/// Member `.` join for short names; same anonymous-parent guard as
228/// [`cpp_join_nested_short`] (#2140).
229fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
230    if parent_short.is_empty() {
231        name.to_string()
232    } else {
233        format!("{parent_short}.{name}")
234    }
235}
236
237/// Structural fq for a leaf declaration: the parent unit's fq plus this
238/// declaration's own name as one segment (or the package segments plus the
239/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
240/// chain, so a literal `$` inside a source identifier (Cython template
241/// substitution points, gcc `$`-identifiers) survives instead of corrupting
242/// the chain and tripping the package/short boundary assert (#2140).
243fn cpp_leaf_fq(
244    package_name: &str,
245    parent: Option<&CodeUnit>,
246    name: &str,
247    kind_if_nested: SegmentKind,
248    kind_if_top: SegmentKind,
249) -> FqName {
250    if let Some(parent) = parent {
251        parent
252            .fq()
253            .clone()
254            .with_pushed(cpp_segment(name, kind_if_nested))
255    } else {
256        let mut fq = FqName::new();
257        cpp_push_package(&mut fq, package_name);
258        fq.push(cpp_segment(name, kind_if_top));
259        fq
260    }
261}
262
263/// Structured name for a member unit (function, field, enumerator). The
264/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
265/// the member has an owner, by `.member`; free functions and globals have no
266/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
267/// C++ member names never contain a literal `.`, so the single `.` (if any)
268/// separates the owner chain from the member.
269pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
270    let mut fq = FqName::new();
271    cpp_push_package(&mut fq, package_name);
272    match short_name.rsplit_once('.') {
273        Some((owner_chain, member)) => {
274            cpp_push_type_chain(&mut fq, owner_chain);
275            fq.push(cpp_segment(member, SegmentKind::Member));
276        }
277        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
278    }
279    fq
280}
281
282/// Structured name for a rendered C++ qualified name supplied at the input
283/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
284///
285/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
286/// it hands back one component for a nested-class chain (`Outer$Inner`). An
287/// exact-name lookup then asks the store for owner `ns` with identifier
288/// `Outer$Inner`, while the declaration persists as a
289/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
290/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
291/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
292/// boundary identical to the persisted one. A `$` that is *not* a nesting join
293/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
294/// and stays reachable through the identifier fallback, which seeks the whole
295/// component text as one spelling.
296pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
297    let mut fq = FqName::new();
298    for component in parse_symbol_path(Language::Cpp, rendered) {
299        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
300        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
301        // construction boundary `cpp_push_type_chain` owns for extraction.
302        let mut first = true;
303        for part in component.split('$').filter(|part| !part.is_empty()) {
304            let kind = if first {
305                // A client-typed head claims no kind, exactly as
306                // `parse_symbol_path_fq` records it.
307                SegmentKind::Unknown
308            } else {
309                SegmentKind::Nested
310            };
311            fq.push(cpp_segment(part, kind));
312            first = false;
313        }
314    }
315    fq
316}
317
318/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
319///
320/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
321/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
322/// `Top.Only`, and the exact-name retain drops the persisted nested identity
323/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
324/// placements extraction stores, so each exact query's parent and terminal
325/// match a persisted row and the structural retain keeps it.
326///
327/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
328/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
329/// is one of those: the `::` there is the package join the canonical name
330/// already spells.
331pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
332    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
333        return Vec::new();
334    }
335    let texts = parse_symbol_path(Language::Cpp, rendered);
336    if texts.len() < 2 {
337        return Vec::new();
338    }
339    cpp_qualified_kind_interpretations(&texts)
340}
341
342/// Every namespace / type / member boundary a `::`-joined source spelling can
343/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
344/// segments.
345fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
346    let len = texts.len();
347    let mut names = Vec::new();
348    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
349    for packages in 0..len {
350        let rest = len - packages;
351        let mut type_chain = Vec::with_capacity(len);
352        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
353        type_chain.push(SegmentKind::Type);
354        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
355        names.push(cpp_fq_with_kinds(texts, type_chain));
356        if rest >= 2 {
357            let mut member = Vec::with_capacity(len);
358            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
359            member.push(SegmentKind::Type);
360            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
361            member.push(SegmentKind::Member);
362            names.push(cpp_fq_with_kinds(texts, member));
363        } else if packages >= 1 {
364            let mut function = vec![SegmentKind::Package; packages];
365            function.push(SegmentKind::Member);
366            names.push(cpp_fq_with_kinds(texts, function));
367        }
368    }
369    names
370}
371
372fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
373    assert_eq!(
374        texts.len(),
375        kinds.len(),
376        "a qualified interpretation names every segment"
377    );
378    let mut fq = FqName::new();
379    for (text, kind) in texts.iter().zip(kinds) {
380        fq.push(cpp_segment(text, kind));
381    }
382    fq
383}
384
385#[derive(Clone)]
386pub struct ScopeInfo {
387    package_name: String,
388    module: Option<CodeUnit>,
389    class_unit: Option<CodeUnit>,
390    template_signature: Option<String>,
391    template_metadata: Option<CppTemplateMetadata>,
392    declarations_are_fields: bool,
393    recovered_specialization_member_scope: bool,
394    /// Namespace targets of every `using namespace X;` directive lexically
395    /// visible at this point in the file (declaration order), threaded
396    /// forward sibling-by-sibling by the sequential container walk (see
397    /// `CppWork::Siblings`). An out-of-line member definition written as a
398    /// bare `Class::method` at file/namespace scope with no enclosing
399    /// `namespace {}` block (issue #1093, e.g. log4cxx's
400    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
401    /// const { ... }`) has no other structural signal for which namespace
402    /// actually owns `Class`; this is the best-effort candidate list used to
403    /// recover it so the definition's indexed identity matches its header
404    /// declaration's.
405    visible_using_namespaces: Vec<String>,
406}
407
408struct CppContainer<'tree> {
409    node: Node<'tree>,
410    scope: ScopeInfo,
411}
412
413struct CppNodeWork<'tree> {
414    node: Node<'tree>,
415    scope: ScopeInfo,
416}
417
418/// Cursor over one container's remaining named children, processed one at a
419/// time (rather than all at once) so a `using namespace X;` sibling can
420/// update `scope.visible_using_namespaces` for the siblings that follow it,
421/// matching real C++ using-directive semantics. Nested container work is
422/// still pushed and fully drained before the cursor resumes (stack LIFO
423/// order), preserving the original left-to-right visitation order.
424struct CppSiblingsWork<'tree> {
425    children: std::vec::IntoIter<Node<'tree>>,
426    scope: ScopeInfo,
427}
428
429enum CppWork<'tree> {
430    Container(CppContainer<'tree>),
431    Node(CppNodeWork<'tree>),
432    Siblings(CppSiblingsWork<'tree>),
433}
434
435fn class_like_name<'tree>(
436    node: Node<'tree>,
437    source: &str,
438    ancestry: &ParentIndex<'tree>,
439) -> Option<String> {
440    let best = class_like_name_from_children(node, source);
441    if let Some(parent) = ancestry.parent(node)
442        && matches!(
443            parent.kind(),
444            "declaration" | "field_declaration" | "function_definition"
445        )
446        // A class_specifier carrying its own body proves the grammar name is
447        // the real class name: a sibling declarator then declares an object
448        // (`class X {} x;`), never a displaced class name. The gate matters
449        // when the class name is itself an all-caps token (`X`, `API`) --
450        // without it the export-macro re-read below steals the object
451        // declarator's name for the class (#2283). The genuine export-macro
452        // shapes leave the class_specifier bodyless, the same invariant
453        // recover_malformed_exported_multiple_base_class already gates on.
454        && cpp_body_node(node).is_none()
455        && node
456            .child_by_field_name("name")
457            .map(|name_node| {
458                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
459            })
460            .unwrap_or(false)
461        && let Some(recovered) = exported_class_name_from_node(parent, source)
462        && best.as_deref() != Some(recovered.as_str())
463    {
464        return Some(recovered);
465    }
466    best.or_else(|| {
467        node.child_by_field_name("name")
468            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
469            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
470    })
471}
472
473fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
474    let mut grammar_name = None;
475    if let Some(name_node) = node.child_by_field_name("name") {
476        let name = normalize_cpp_whitespace(node_text(name_node, source));
477        if name.is_empty() {
478            return None;
479        }
480        if !cpp_export_macro_token(&name) {
481            return Some(name);
482        }
483        grammar_name = Some(name);
484    }
485
486    let mut best = None;
487    let mut cursor = node.walk();
488    let mut stack = Vec::new();
489    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
490        if matches!(
491            child.kind(),
492            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
493        ) {
494            break;
495        }
496        stack.push(child);
497    }
498
499    while let Some(current) = stack.pop() {
500        if matches!(current.kind(), "type_identifier" | "identifier") {
501            let name = normalize_cpp_whitespace(node_text(current, source));
502            if !name.is_empty() && !cpp_export_macro_token(&name) {
503                best = Some(name);
504            }
505            continue;
506        }
507
508        push_named_children_reversed(current, &mut stack);
509    }
510    best.or(grammar_name)
511}
512
513pub fn cpp_export_macro_token(token: &str) -> bool {
514    token
515        .chars()
516        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
517}
518
519struct RecoveredExportedClass<'tree> {
520    declaration_node: Node<'tree>,
521    name: String,
522    body: Option<Node<'tree>>,
523    raw_supertypes: Option<Vec<String>>,
524    base_facts: Option<Vec<CppBaseSpecifierFact>>,
525    uses_initializer_body: bool,
526    /// Present only for the fragmented multiple-base export shape (issue #938).
527    /// Carries the true class-body byte region -- the members tree-sitter scattered
528    /// out of the recovered node -- so they can be reparsed and re-owned as members
529    /// rather than lost inside the truncated `initializer_list` stand-in.
530    fragmented_body: Option<FragmentedExportBody>,
531}
532
533struct RecoveredFunctionLikeExportClassPair {
534    name: String,
535    range: Range,
536    raw_supertypes: Option<Vec<String>>,
537    base_facts: Option<Vec<CppBaseSpecifierFact>>,
538    fragmented_body: FragmentedExportBody,
539}
540
541struct RecoveredEmbeddedFunctionLikeExportClass {
542    name: String,
543    range: Range,
544    raw_supertypes: Vec<String>,
545    base_facts: Vec<CppBaseSpecifierFact>,
546    fragmented_body: FragmentedExportBody,
547}
548
549/// The recovered class-body geometry for a fragmented multiple-base export class.
550/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
551/// verbatim for a region reparse (issue #941 machinery) so every recovered member
552/// keeps its exact original byte/line position. `class_range` is the full class
553/// navigation range spanning to the displaced closing brace.
554struct FragmentedExportBody {
555    reparse_start: usize,
556    reparse_end: usize,
557    class_range: Range,
558}
559
560fn recovered_fragmented_export_body(
561    body: Node<'_>,
562    class_range: Range,
563) -> Option<FragmentedExportBody> {
564    let open = body.child(0).filter(|child| child.kind() == "{")?;
565    let close = body
566        .child(body.child_count().saturating_sub(1))
567        .filter(|child| child.kind() == "}" && !child.is_missing());
568    Some(FragmentedExportBody {
569        reparse_start: open.end_byte(),
570        // A zero-width missing `}` contributes no source byte. Keep the whole
571        // body range in that case; subtracting one byte would discard the last
572        // member's semicolon and make the otherwise valid region unsafe to
573        // index. A real close token is excluded by its structured start.
574        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
575        class_range,
576    })
577}
578
579struct DisplacedFragmentNamespaceBoundary<'tree> {
580    class_close: Node<'tree>,
581    class_semicolon: Node<'tree>,
582    namespace_items: Vec<Node<'tree>>,
583}
584
585/// Result of validating a reparsed fragmented class body.  A complete tree can
586/// safely consume the whole region.  A partial tree may contain only the exact
587/// class-named constructor that tree-sitter merged into an access label; its
588/// remaining siblings must stay on the ordinary outer walk.
589enum FragmentedExportMembers {
590    Complete(Tree),
591    ConditionalConstructor(Tree),
592}
593
594#[derive(Clone, Copy)]
595struct DisplacedMacroClassTail {
596    split_index: usize,
597    class_range: Range,
598}
599
600fn recover_exported_class_declaration<'tree>(
601    node: Node<'tree>,
602    source: &str,
603) -> Option<RecoveredExportedClass<'tree>> {
604    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
605        return Some(recovered);
606    }
607
608    let class_node = first_class_like_child(node)?;
609    if let Some(name_node) = class_node.child_by_field_name("name") {
610        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
611        if cpp_export_macro_token(&class_name) {
612            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
613            // Name declarator. Only a bare declarator can be the displaced class name;
614            // wrappers describe an object whose type merely happens to look macro-like.
615            let mut cursor = node.walk();
616            if node
617                .children_by_field_name("declarator", &mut cursor)
618                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
619            {
620                return None;
621            }
622        } else if has_direct_cpp_declarator(node) {
623            return None;
624        }
625    }
626    let name = exported_class_name_from_node(class_node, source)?;
627    Some(RecoveredExportedClass {
628        declaration_node: class_node,
629        name,
630        body: cpp_body_node(class_node),
631        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
632            .then(|| extract_cpp_supertypes(class_node, source)),
633        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
634            .then(|| extract_cpp_base_facts(class_node, source)),
635        uses_initializer_body: false,
636        fragmented_body: None,
637    })
638}
639
640fn recover_malformed_exported_base_class<'tree>(
641    node: Node<'tree>,
642    source: &str,
643) -> Option<RecoveredExportedClass<'tree>> {
644    if node.kind() != "declaration" {
645        return None;
646    }
647    let class_node = node.child_by_field_name("type")?;
648    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
649        return None;
650    }
651    let macro_name = class_node
652        .child_by_field_name("name")
653        .and_then(|name| direct_identifier_name(name, source))?;
654    if !cpp_export_macro_token(&macro_name) {
655        return None;
656    }
657
658    let mut named_cursor = node.walk();
659    let mut named = node.named_children(&mut named_cursor);
660    if named
661        .next()
662        .is_none_or(|child| !same_node(child, class_node))
663    {
664        return None;
665    }
666    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
667    if displaced.kind() != "ERROR" {
668        return None;
669    }
670    let name = displaced_exported_class_name(displaced, source)?;
671
672    let remaining = named.collect::<Vec<_>>();
673    let init = *remaining.last()?;
674    if init.kind() != "init_declarator" {
675        return None;
676    }
677    let final_base = init
678        .child_by_field_name("declarator")
679        .and_then(|base| recovered_malformed_base_name(base, source))?;
680    let body = init.child_by_field_name("value")?;
681    // A complete reduction has a real closing brace here. In Chromium's Widget
682    // declaration, tree-sitter instead emits the same direct `}` slot as a
683    // zero-width missing node where the first body macro truncates the prefix.
684    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
685        return None;
686    }
687
688    if remaining[..remaining.len() - 1]
689        .iter()
690        .any(|child| match child.kind() {
691            "qualified_identifier"
692            | "scoped_type_identifier"
693            | "type_identifier"
694            | "identifier" => false,
695            "ERROR" => !is_malformed_inheritance_access(*child, source),
696            _ => true,
697        })
698    {
699        return None;
700    }
701
702    let mut raw_supertypes = Vec::new();
703    let mut base_facts = Vec::new();
704    for base in &remaining[..remaining.len() - 1] {
705        if base.kind() == "ERROR" {
706            continue;
707        }
708        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
709        base_facts.push(recovered_base_specifier(
710            *base,
711            source,
712            recovered_base_is_virtual(node, *base, source),
713        )?);
714    }
715    raw_supertypes.push(final_base);
716    let final_base_node = init.child_by_field_name("declarator")?;
717    base_facts.push(recovered_base_specifier(
718        final_base_node,
719        source,
720        recovered_base_is_virtual(init, final_base_node, source),
721    )?);
722
723    Some(RecoveredExportedClass {
724        declaration_node: node,
725        name,
726        body: Some(body),
727        raw_supertypes: Some(raw_supertypes),
728        base_facts: Some(base_facts),
729        uses_initializer_body: true,
730        fragmented_body: fragmented_export_body_region(node, body, source),
731    })
732}
733
734/// Locate the true class-body region for a fragmented multiple-base export class.
735///
736/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
737/// emits in place of the real class body. Tree-sitter reduces that body in one of
738/// two shapes, both of which lose the members from the recovered node:
739///
740/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
741///   a real closing brace and holds the whole body text inline. The interior between
742///   the braces reparses to the members directly.
743/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
744///   first member with a zero-width MISSING `}`; every later member -- and the real
745///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
746///   siblings. The interior runs from the opening brace to that displaced `}`.
747///
748/// Returns the interior byte range to reparse plus the full class navigation range.
749fn fragmented_export_body_region(
750    node: Node<'_>,
751    body: Node<'_>,
752    source: &str,
753) -> Option<FragmentedExportBody> {
754    let reparse_start = body.start_byte() + 1;
755    let close = direct_close_brace(body)?;
756    if close.end_byte() > close.start_byte() {
757        return Some(FragmentedExportBody {
758            reparse_start,
759            reparse_end: close.start_byte(),
760            class_range: cpp_declaration_range(node),
761        });
762    }
763    // The closing brace was displaced past the recovered node. A balanced nested
764    // class keeps its own braces, so the first lone-`}` sibling is this class's.
765    let mut sibling = node.next_named_sibling();
766    let displaced_close = loop {
767        let Some(current) = sibling else {
768            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
769        };
770        if cpp_is_stray_close_brace(current, source) {
771            break current;
772        }
773        sibling = current.next_named_sibling();
774    };
775    Some(FragmentedExportBody {
776        reparse_start,
777        reparse_end: displaced_close.start_byte(),
778        class_range: Range {
779            start_byte: node.start_byte(),
780            end_byte: displaced_close.end_byte(),
781            start_line: node.start_position().row + 1,
782            end_line: displaced_close.end_position().row + 1,
783        },
784    })
785}
786
787/// Locate the true class-body region for the export-macro class shape that
788/// tree-sitter promotes to a `function_definition`.
789///
790/// In this shape the synthetic function body closes at the first inline
791/// method, while the class's real members continue as root-level siblings until
792/// a stray `}` followed by the displaced class `;`. Reparse the complete
793/// interior so those siblings are visited with the recovered class scope.
794fn fragmented_export_function_body_region(
795    node: Node<'_>,
796    body: Node<'_>,
797    source: &str,
798    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
799) -> Option<FragmentedExportBody> {
800    let reparse_start = body.start_byte().checked_add(1)?;
801    if let Some(boundary) = displaced_namespace {
802        return Some(FragmentedExportBody {
803            reparse_start,
804            reparse_end: boundary.class_close.start_byte(),
805            class_range: Range {
806                start_byte: node.start_byte(),
807                end_byte: boundary.class_semicolon.end_byte(),
808                start_line: node.start_position().row + 1,
809                end_line: boundary.class_semicolon.end_position().row + 1,
810            },
811        });
812    }
813    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
814    let boundary =
815        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
816    let boundary_index = boundary.and_then(|boundary| {
817        siblings
818            .iter()
819            .position(|candidate| same_node(*candidate, boundary))
820    });
821    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
822    let mut sibling_index = 0;
823    // A complete recovered class's synthetic wrapper is immediately followed
824    // by its displaced semicolon (comments and a trailing attribute macro --
825    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
826    // may sit between the body and that semicolon). Only scan for a later
827    // stray close when real member siblings intervene; otherwise every earlier
828    // complete class would borrow the next malformed class's close and claim
829    // its members. The trailing-attribute case is the gtest shape: the scan
830    // borrowed a close ~1900 lines later and re-owned a following
831    // `namespace testing { namespace internal {` block as class members,
832    // doubling the package path ("testing::internal::testing::internal") and
833    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
834    // boundary assert (#2297).
835    while let Some(current) = siblings.get(sibling_index).copied() {
836        if current.kind() == "comment" {
837            sibling_index += 1;
838            continue;
839        }
840        if is_trailing_attribute_macro_sibling(current) {
841            sibling_index += 1;
842            continue;
843        }
844        if cpp_is_stray_semicolon(current, source) {
845            return None;
846        }
847        break;
848    }
849    while let Some(current) = siblings.get(sibling_index).copied() {
850        let next = siblings.get(sibling_index + 1).copied();
851        if cpp_is_stray_close_brace(current, source)
852            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
853        {
854            let semicolon = next.expect("checked above");
855            return Some(FragmentedExportBody {
856                reparse_start,
857                reparse_end: current.start_byte(),
858                class_range: Range {
859                    start_byte: node.start_byte(),
860                    end_byte: semicolon.end_byte(),
861                    start_line: node.start_position().row + 1,
862                    end_line: semicolon.end_position().row + 1,
863                },
864            });
865        }
866        // When the final access label keeps the class close in its malformed
867        // declaration body, tree-sitter nests the lone `}` ERROR below the
868        // label instead of exposing it as a direct sibling. Search only the
869        // scattered siblings after the synthetic wrapper. The first such
870        // close is the class terminator because nested class bodies retain
871        // their own balanced class_specifier nodes.
872        if current.start_byte() >= body.end_byte()
873            && let Some(close) = cpp_nested_stray_close_brace(current, source)
874        {
875            return Some(FragmentedExportBody {
876                reparse_start,
877                reparse_end: close.start_byte(),
878                class_range: Range {
879                    start_byte: node.start_byte(),
880                    end_byte: current.end_byte(),
881                    start_line: node.start_position().row + 1,
882                    end_line: current.end_position().row + 1,
883                },
884            });
885        }
886        sibling_index += 1;
887    }
888    boundary.map(|boundary| FragmentedExportBody {
889        reparse_start,
890        reparse_end: boundary.start_byte(),
891        class_range: Range {
892            start_byte: node.start_byte(),
893            end_byte: boundary.start_byte(),
894            start_line: node.start_position().row + 1,
895            end_line: boundary.start_position().row + 1,
896        },
897    })
898}
899
900/// Find a later macro-export class that tree-sitter lifted through an enclosing
901/// preprocessor container. A class that is still a direct sibling can be a
902/// nested member of the current fragmented class, so only a changed parent is
903/// a proven boundary between the two recovered class envelopes.
904fn fragmented_export_sibling_class_boundary<'tree>(
905    node: Node<'tree>,
906    source: &str,
907    ancestry: &ParentIndex<'tree>,
908) -> Option<Node<'tree>> {
909    let node_parent = ancestry.parent(node)?;
910    cpp_following_named_siblings(node, source, ancestry)
911        .into_iter()
912        .find(|candidate| {
913            recover_exported_class_function_definition(*candidate, source).is_some()
914                && ancestry
915                    .parent(*candidate)
916                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
917        })
918}
919
920/// A trailing attribute macro after a recovered class's closing brace, spelled
921/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
922/// bare identifier is never a class member (members need a type), so this
923/// sibling can only be the class's own tail (#2297).
924fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
925    if node.kind() != "expression_statement" {
926        return false;
927    }
928    let mut cursor = node.walk();
929    let mut children = node.named_children(&mut cursor);
930    children
931        .next()
932        .is_some_and(|child| child.kind() == "identifier")
933        && children.next().is_none()
934}
935
936/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
937/// export-class wrapper can place the class close inside an access-label node,
938/// so direct-sibling checks alone miss the boundary.  Walk named CST children
939/// only; the helper does not inspect source text beyond the existing structured
940/// stray-brace predicate.
941fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
942    let mut stack = vec![node];
943    while let Some(current) = stack.pop() {
944        if cpp_is_stray_close_brace(current, source) {
945            return Some(current);
946        }
947        let mut cursor = current.walk();
948        stack.extend(current.named_children(&mut cursor));
949    }
950    None
951}
952
953/// Return named siblings that follow `node`, including siblings that tree-sitter
954/// attached to an enclosing container after malformed recovery split the local
955/// declaration list. Stop at the first structurally visible class close so a
956/// later namespace or exported class cannot supply the recovery boundary.
957///
958/// The climb takes a [`ParentIndex`] because it runs once per visited node and
959/// walks every level to the root: with `Node::parent`, which re-descends from
960/// the root on each call, a translation unit holding thousands of siblings --
961/// generated instruction tables, for instance -- made this the whole cost of
962/// indexing the file. Callers inside the declaration walk pass the walk's
963/// index; the four that ask from outside a traversal pass
964/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
965/// behaviour is unchanged.
966fn cpp_following_named_siblings<'tree>(
967    node: Node<'tree>,
968    source: &str,
969    ancestry: &ParentIndex<'tree>,
970) -> Vec<Node<'tree>> {
971    let mut siblings = Vec::new();
972    let mut anchor = node;
973    while let Some(parent) = ancestry.parent(anchor) {
974        let at_translation_unit = parent.kind() == "translation_unit";
975        let mut cursor = parent.walk();
976        assert!(
977            cursor.goto_first_child(),
978            "ParentIndex returned a parent without children"
979        );
980        while !same_node(cursor.node(), anchor) {
981            assert!(
982                cursor.goto_next_sibling(),
983                "ParentIndex parent does not contain its child anchor"
984            );
985        }
986        while cursor.goto_next_sibling() {
987            let current = cursor.node();
988            if !current.is_named() {
989                continue;
990            }
991            if at_translation_unit
992                && (current.kind() == "namespace_definition"
993                    || (current.kind() == "function_definition"
994                        && first_class_like_child(current).is_some()))
995            {
996                return siblings;
997            }
998            siblings.push(current);
999            if cpp_is_stray_close_brace(current, source) {
1000                if let Some(semicolon) = current
1001                    .next_named_sibling()
1002                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1003                {
1004                    siblings.push(semicolon);
1005                }
1006                return siblings;
1007            }
1008            if current.start_byte() >= node.end_byte()
1009                && matches!(current.kind(), "ERROR" | "labeled_statement")
1010                && cpp_nested_stray_close_brace(current, source).is_some()
1011            {
1012                return siblings;
1013            }
1014        }
1015        anchor = parent;
1016    }
1017    siblings
1018}
1019
1020fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1021    if node.start_byte() >= class_end {
1022        return false;
1023    }
1024    node.end_byte() <= class_end
1025        || cpp_nested_stray_close_brace(node, source)
1026            .is_some_and(|close| close.start_byte() == class_end)
1027}
1028
1029/// A class whose head tree-sitter kept in one malformed container while the
1030/// members after the first and the closing `};` scattered across that
1031/// container's following siblings.
1032struct FragmentedClassRecovery<'tree> {
1033    declaration_node: Node<'tree>,
1034    name: String,
1035    raw_supertypes: Vec<String>,
1036    body: FragmentedExportBody,
1037}
1038
1039/// Recover a class whose opening prefix is retained in one ERROR node while one
1040/// or more nested class closes and the outer close are displaced to sibling
1041/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1042/// tokens establish nesting depth and the displaced close nodes terminate it.
1043///
1044/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1045/// `class` keyword as the container's first token. A function-like export macro
1046/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1047/// body-less `class_specifier` named after the macro followed by the
1048/// invocation's `(`, arguments and `)`, with the real class name and the base
1049/// clause left as bare tokens after it (#2924).
1050fn fragmented_class_body<'tree>(
1051    node: Node<'tree>,
1052    source: &str,
1053) -> Option<FragmentedClassRecovery<'tree>> {
1054    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1055        return Some(recovered);
1056    }
1057    let supported_container = node.kind() == "ERROR"
1058        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1059    if !supported_container {
1060        return None;
1061    }
1062    let mut cursor = node.walk();
1063    let children = node.children(&mut cursor).collect::<Vec<_>>();
1064    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1065        return Some(recovered);
1066    }
1067    let keyword = children.first()?;
1068    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1069        return None;
1070    }
1071    let name_node = children
1072        .iter()
1073        .copied()
1074        .skip(1)
1075        .find(|child| child.is_named())?;
1076    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1077        return None;
1078    }
1079    let name = normalize_cpp_whitespace(node_text(name_node, source));
1080    if name.is_empty() || cpp_export_macro_token(&name) {
1081        return None;
1082    }
1083    let open_index = children.iter().position(|child| child.kind() == "{")?;
1084    Some(FragmentedClassRecovery {
1085        declaration_node: node,
1086        name,
1087        raw_supertypes: extract_cpp_supertypes(node, source),
1088        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
1089    })
1090}
1091
1092/// The fragmented body of a function-like export-macro class head that
1093/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1094/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1095/// public virtual EC_PublicKey {`, #2924).
1096///
1097/// The class name is read positionally by [`recovered_export_head_name`] -- the
1098/// last identifier before the head ends at `final`, at the base clause `:`, or
1099/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1100/// spelled with `virtual`, with `final`, or with qualified base names needs no
1101/// arm of its own.
1102fn fragmented_export_macro_class_body<'tree>(
1103    node: Node<'tree>,
1104    children: &[Node<'tree>],
1105    source: &str,
1106) -> Option<FragmentedClassRecovery<'tree>> {
1107    let class_node = *children.first()?;
1108    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1109        return None;
1110    }
1111    // The identifier tree-sitter took for the class name is the export macro, a
1112    // function-like invocation when the `(` of its argument list follows it
1113    // directly. Spelling plays no part (#2557).
1114    class_node
1115        .child_by_field_name("name")
1116        .and_then(|name| direct_identifier_name(name, source))?;
1117    let invocation = *children.get(1)?;
1118    if invocation.is_named() || invocation.kind() != "(" {
1119        return None;
1120    }
1121    let open_index = children
1122        .iter()
1123        .position(|child| !child.is_named() && child.kind() == "{")?;
1124    let open = children[open_index];
1125    let name_node = recovered_export_head_name(node, open, source)?;
1126    let name = normalize_cpp_whitespace(node_text(name_node, source));
1127    if name.is_empty() || cpp_export_macro_token(&name) {
1128        return None;
1129    }
1130    Some(FragmentedClassRecovery {
1131        declaration_node: node,
1132        name,
1133        raw_supertypes: recovered_export_head_bases(
1134            node,
1135            name_node.end_byte(),
1136            open.start_byte(),
1137            source,
1138        ),
1139        body: fragmented_displaced_class_body(node, children, open_index, source)
1140            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1141    })
1142}
1143
1144/// The class body a fragmented head opened at `children[open_index]` but could
1145/// not close: tree-sitter scattered the remaining members and the closing `};`
1146/// across the container's following siblings. The direct class tokens after the
1147/// opening brace say how many closes the nested classes consume before this
1148/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1149fn fragmented_displaced_class_body(
1150    node: Node<'_>,
1151    children: &[Node<'_>],
1152    open_index: usize,
1153    source: &str,
1154) -> Option<FragmentedExportBody> {
1155    let open = children[open_index];
1156    let nested_class_opens = children[open_index + 1..]
1157        .iter()
1158        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1159        .count();
1160    let mut closes_remaining = 1 + nested_class_opens;
1161    let mut sibling = node.next_named_sibling();
1162    while let Some(candidate) = sibling {
1163        let next = candidate.next_named_sibling();
1164        if cpp_is_stray_close_brace(candidate, source) {
1165            closes_remaining -= 1;
1166            if closes_remaining == 0 {
1167                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1168                if open.end_byte() >= candidate.start_byte() {
1169                    return None;
1170                }
1171                return Some(FragmentedExportBody {
1172                    reparse_start: open.end_byte(),
1173                    reparse_end: candidate.start_byte(),
1174                    class_range: Range {
1175                        start_byte: node.start_byte(),
1176                        end_byte: semicolon.end_byte(),
1177                        start_line: node.start_position().row + 1,
1178                        end_line: semicolon.end_position().row + 1,
1179                    },
1180                });
1181            }
1182        }
1183        sibling = next;
1184    }
1185    None
1186}
1187
1188/// The body of a fragmented export-macro class head whose close tree-sitter
1189/// spent on the enclosing container instead: Botan's
1190/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1191/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1192/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1193/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1194/// (#2924).
1195fn fragmented_container_close_class_body(
1196    node: Node<'_>,
1197    open: Node<'_>,
1198    source: &str,
1199) -> Option<FragmentedExportBody> {
1200    let parent = node.parent()?;
1201    if !matches!(
1202        parent.kind(),
1203        "declaration_list" | "field_declaration_list" | "compound_statement"
1204    ) {
1205        return None;
1206    }
1207    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1208    // The container's close is this class's close only when the body's own
1209    // brace balance says so. Without that cross-check -- the one issue #1524
1210    // already uses on a mis-closed wrapper body -- a class that really does end
1211    // earlier would claim every later container-level declaration as a member.
1212    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1213        || open.end_byte() >= close.start_byte()
1214    {
1215        return None;
1216    }
1217    Some(FragmentedExportBody {
1218        reparse_start: open.end_byte(),
1219        reparse_end: close.start_byte(),
1220        class_range: Range {
1221            start_byte: node.start_byte(),
1222            end_byte: close.end_byte(),
1223            start_line: node.start_position().row + 1,
1224            end_line: close.end_position().row + 1,
1225        },
1226    })
1227}
1228
1229pub(crate) fn recovered_fragmented_class_has_body(
1230    node: Node<'_>,
1231    source: &str,
1232    expected_name: &str,
1233    expected_range: &Range,
1234) -> bool {
1235    fragmented_class_body(node, source).is_some_and(|recovered| {
1236        recovered.name == expected_name
1237            && recovered.body.class_range.start_byte == expected_range.start_byte
1238            && recovered.body.class_range.end_byte == expected_range.end_byte
1239    })
1240}
1241
1242/// Recover a plain class whose parser-visible body ends inside a malformed
1243/// inline member. Tree-sitter then attaches either the next real member
1244/// declarator or the unfinished `else` branch directly to the outer function
1245/// definition and leaves the class's actual `};` among later siblings. Those
1246/// structured continuations and the close/semicolon siblings establish the
1247/// complete body envelope without interpreting source text.
1248fn fragmented_plain_class_declaration_body<'tree>(
1249    node: Node<'tree>,
1250    source: &str,
1251) -> Option<FragmentedClassRecovery<'tree>> {
1252    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1253        return None;
1254    }
1255    let class_node = node.child_by_field_name("type")?;
1256    if !matches!(
1257        class_node.kind(),
1258        "class_specifier" | "struct_specifier" | "union_specifier"
1259    ) {
1260        return None;
1261    }
1262    let name_node = class_node.child_by_field_name("name")?;
1263    let name = normalize_cpp_whitespace(node_text(name_node, source));
1264    if name.is_empty() || cpp_export_macro_token(&name) {
1265        return None;
1266    }
1267    let body = cpp_body_node(class_node)?;
1268    if body.kind() != "field_declaration_list" {
1269        return None;
1270    }
1271    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1272        if declarator.start_byte() < class_node.end_byte() {
1273            return None;
1274        }
1275        let mut cursor = node.walk();
1276        node.named_children(&mut cursor).any(|child| {
1277            if child.kind() != "ERROR"
1278                || child.start_byte() < class_node.end_byte()
1279                || child.end_byte() > declarator.start_byte()
1280            {
1281                return false;
1282            }
1283            let mut cursor = child.walk();
1284            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1285            let Some((return_type, attributes)) = components.split_last() else {
1286                return false;
1287            };
1288            matches!(
1289                return_type.kind(),
1290                "identifier"
1291                    | "type_identifier"
1292                    | "primitive_type"
1293                    | "decltype"
1294                    | "placeholder_type_specifier"
1295            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1296                && attributes.iter().all(|attribute| {
1297                    matches!(attribute.kind(), "identifier" | "type_identifier")
1298                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1299                            *attribute, source,
1300                        )))
1301                })
1302        })
1303    } else {
1304        let mut cursor = node.walk();
1305        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1306        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1307            if same_node(*candidate_class, class_node)
1308                && continuation.kind() == "identifier"
1309                && node_text(*continuation, source) == "else"
1310                && continuation_body.kind() == "compound_statement"
1311                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1312                && continuation_body
1313                    .child(continuation_body.child_count().saturating_sub(1))
1314                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1315    };
1316    if !displaced_member {
1317        return None;
1318    }
1319    let open = body
1320        .children(&mut body.walk())
1321        .find(|child| child.kind() == "{")?;
1322    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1323    let ordinary_boundary =
1324        siblings
1325            .iter()
1326            .copied()
1327            .enumerate()
1328            .find_map(|(close_index, close)| {
1329                cpp_is_stray_close_brace(close, source)
1330                    .then(|| {
1331                        siblings
1332                            .get(close_index + 1)
1333                            .copied()
1334                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1335                            .map(|semicolon| (close, semicolon))
1336                    })
1337                    .flatten()
1338            });
1339    let (close, semicolon) =
1340        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1341            (boundary.class_close, boundary.class_semicolon)
1342        } else {
1343            ordinary_boundary?
1344        };
1345    if open.end_byte() >= close.start_byte() {
1346        return None;
1347    }
1348    Some(FragmentedClassRecovery {
1349        declaration_node: class_node,
1350        name,
1351        raw_supertypes: extract_cpp_supertypes(class_node, source),
1352        body: FragmentedExportBody {
1353            reparse_start: open.end_byte(),
1354            reparse_end: close.start_byte(),
1355            class_range: Range {
1356                start_byte: class_node.start_byte(),
1357                end_byte: semicolon.end_byte(),
1358                start_line: class_node.start_position().row + 1,
1359                end_line: semicolon.end_position().row + 1,
1360            },
1361        },
1362    })
1363}
1364
1365fn displaced_export_function_namespace_shape<'tree>(
1366    declaration: Node<'tree>,
1367    source: &str,
1368) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1369    let mut nested = Vec::new();
1370    for index in (0..declaration.named_child_count()).rev() {
1371        nested.push(declaration.named_child(index)?);
1372    }
1373    while let Some(current) = nested.pop() {
1374        // A recovered export class nested in this class can consume the first
1375        // parser-visible namespace close itself. In that shape the existing
1376        // later-class boundary logic already distinguishes the nested and
1377        // namespace-sibling owners; do not mistake the nested close for this
1378        // class's terminator.
1379        if recover_exported_class_function_definition(current, source).is_some() {
1380            return None;
1381        }
1382        for index in (0..current.named_child_count()).rev() {
1383            nested.push(current.named_child(index)?);
1384        }
1385    }
1386    let mut same_envelope_sibling = declaration.next_named_sibling();
1387    while let Some(current) = same_envelope_sibling {
1388        if recover_exported_class_function_definition(current, source).is_some() {
1389            return None;
1390        }
1391        same_envelope_sibling = current.next_named_sibling();
1392    }
1393    let declaration_list = declaration.parent()?;
1394    if declaration_list.kind() != "declaration_list" {
1395        return None;
1396    }
1397    let namespace = declaration_list.parent()?;
1398    if namespace.kind() != "namespace_definition"
1399        || namespace.child_by_field_name("body") != Some(declaration_list)
1400    {
1401        return None;
1402    }
1403    let class_close = direct_close_brace(declaration_list)?;
1404    let trailing_semicolon = namespace.next_named_sibling()?;
1405    if trailing_semicolon.kind() != "expression_statement"
1406        || trailing_semicolon.named_child_count() != 0
1407    {
1408        return None;
1409    }
1410    // A chain of malformed export classes can consume one parser-visible
1411    // namespace close per class. Walk through the enclosing sibling levels so
1412    // the later real namespace close remains the structural boundary; a
1413    // direct next-sibling walk stops at the first collapsed namespace and
1414    // incorrectly makes its intervening items members of this class.
1415    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1416    let trailing_index = siblings
1417        .iter()
1418        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1419    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1420        recover_exported_class_function_definition(*candidate, source).is_some()
1421    }) {
1422        // Consecutive recovered classes already have an exact sibling-class
1423        // boundary. Preserve that established path, including nested export
1424        // classes, instead of interpreting the first class close as a
1425        // collapsed namespace boundary.
1426        return None;
1427    }
1428    let mut namespace_items = Vec::new();
1429    let mut nested_fragment_end = 0;
1430    for current in siblings.into_iter().skip(trailing_index + 1) {
1431        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1432        {
1433            return Some(DisplacedFragmentNamespaceBoundary {
1434                class_close,
1435                class_semicolon: trailing_semicolon,
1436                namespace_items,
1437            });
1438        }
1439        if current.start_byte() >= nested_fragment_end
1440            && let Some(recovered) = fragmented_class_body(current, source)
1441        {
1442            nested_fragment_end = recovered.body.class_range.end_byte;
1443        } else if current.start_byte() >= nested_fragment_end
1444            && recover_exported_class_function_definition(current, source).is_some()
1445            && let Some(body) = cpp_body_node(current)
1446            && let Some(fragmented) =
1447                fragmented_export_function_body_region(current, body, source, None)
1448        {
1449            nested_fragment_end = fragmented.class_range.end_byte;
1450        }
1451        namespace_items.push(current);
1452    }
1453    None
1454}
1455
1456fn displaced_fragment_namespace_boundary<'tree>(
1457    declaration: Node<'tree>,
1458    body: Node<'tree>,
1459    source: &str,
1460) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1461    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1462    let reparse_start = body.start_byte() + 1;
1463    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1464    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1465}
1466
1467/// Recover the class/namespace brace geometry for a declaration whose class
1468/// close tree-sitter consumed as the enclosing namespace close. This proof is
1469/// independent of whether every member in the class body can be reparsed: the
1470/// ordinary-tree fallback can still re-own bounded sibling declarations when
1471/// an unknown macro makes the complete body reparse unsafe.
1472fn displaced_fragment_namespace_geometry<'tree>(
1473    declaration: Node<'tree>,
1474    source: &str,
1475) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1476    // A templated class's malformed function wrapper remains beneath the
1477    // template node even though its later members have escaped to the
1478    // enclosing declaration list. Lift only that exact declaration child.
1479    let envelope = declaration
1480        .parent()
1481        .filter(|parent| {
1482            parent.kind() == "template_declaration"
1483                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1484        })
1485        .unwrap_or(declaration);
1486    let declaration_list = envelope.parent()?;
1487    if declaration_list.kind() != "declaration_list" {
1488        return None;
1489    }
1490    let namespace = declaration_list.parent()?;
1491    if namespace.kind() != "namespace_definition"
1492        || namespace.child_by_field_name("body") != Some(declaration_list)
1493    {
1494        return None;
1495    }
1496    let class_close = direct_close_brace(declaration_list)?;
1497    let trailing_semicolon = namespace.next_named_sibling()?;
1498    if trailing_semicolon.kind() != "expression_statement"
1499        || trailing_semicolon.named_child_count() != 0
1500    {
1501        return None;
1502    }
1503    let mut namespace_items = Vec::new();
1504    let mut sibling = trailing_semicolon.next_named_sibling();
1505    let mut nested_fragment_end = 0;
1506    loop {
1507        let current = sibling?;
1508        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1509        {
1510            break;
1511        }
1512        if current.start_byte() >= nested_fragment_end
1513            && let Some(recovered) = fragmented_class_body(current, source)
1514        {
1515            nested_fragment_end = recovered.body.class_range.end_byte;
1516        }
1517        namespace_items.push(current);
1518        sibling = current.next_named_sibling();
1519    }
1520    Some(DisplacedFragmentNamespaceBoundary {
1521        class_close,
1522        class_semicolon: trailing_semicolon,
1523        namespace_items,
1524    })
1525}
1526
1527/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1528fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1529    (0..node.child_count())
1530        .filter_map(|index| node.child(index))
1531        .find(|child| !child.is_named() && child.kind() == "}")
1532}
1533
1534/// A displaced lone closing brace: the class close that the fragmented multiple-base
1535/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1536fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1537    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1538}
1539
1540/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1541/// text while skipping line/block comments and string/char literals. The
1542/// exported-class recovery needs this when tree-sitter's bogus
1543/// `function_definition` body runs past the class's true closing brace and
1544/// swallows following siblings (issue #1524): the grammar tree carries no
1545/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1546/// close is located textually. Returns `None` when the text is unbalanced or
1547/// contains a construct the scanner deliberately does not interpret (raw
1548/// strings) -- callers treat that as "cannot partition" and keep the
1549/// un-split recovery.
1550fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1551    let bytes = source.as_bytes();
1552    if bytes.get(open_byte) != Some(&b'{') {
1553        return None;
1554    }
1555    let mut depth = 0usize;
1556    let mut i = open_byte;
1557    while i < bytes.len() {
1558        match bytes[i] {
1559            b'{' => depth += 1,
1560            b'}' => {
1561                depth = depth.checked_sub(1)?;
1562                if depth == 0 {
1563                    return Some(i);
1564                }
1565            }
1566            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1567                while i < bytes.len() && bytes[i] != b'\n' {
1568                    i += 1;
1569                }
1570                continue;
1571            }
1572            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1573                i += 2;
1574                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1575                    i += 1;
1576                }
1577                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1578                continue;
1579            }
1580            quote @ (b'"' | b'\'') => {
1581                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1582                // bail out rather than mis-count.
1583                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1584                    return None;
1585                }
1586                i += 1;
1587                while i < bytes.len() && bytes[i] != quote {
1588                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1589                }
1590                if i >= bytes.len() {
1591                    return None;
1592                }
1593            }
1594            _ => {}
1595        }
1596        i += 1;
1597    }
1598    None
1599}
1600
1601fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1602    let mut name = None;
1603    let mut colon_count = 0;
1604    let mut access_count = 0;
1605    for index in 0..node.child_count() {
1606        let child = node.child(index)?;
1607        match child.kind() {
1608            "identifier" | "type_identifier" if child.is_named() => {
1609                if name.is_some() {
1610                    return None;
1611                }
1612                let candidate = normalize_cpp_whitespace(node_text(child, source));
1613                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1614                    return None;
1615                }
1616                name = Some(candidate);
1617            }
1618            "template_function" | "template_type" if child.is_named() => {
1619                if name.is_some() {
1620                    return None;
1621                }
1622                let candidate = child
1623                    .child_by_field_name("name")
1624                    .and_then(|name| direct_identifier_name(name, source))?;
1625                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1626                    return None;
1627                }
1628                name = Some(candidate);
1629            }
1630            ":" if !child.is_named() => colon_count += 1,
1631            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1632            _ => return None,
1633        }
1634    }
1635    (colon_count == 1 && access_count == 1)
1636        .then_some(name)
1637        .flatten()
1638}
1639
1640fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1641    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1642        return false;
1643    }
1644    node.named_child(0)
1645        .and_then(|child| direct_identifier_name(child, source))
1646        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1647}
1648
1649fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1650    (0..node.child_count()).any(|index| {
1651        node.child(index)
1652            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1653    })
1654}
1655
1656fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1657    match node.kind() {
1658        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1659            recovered_base_atom(node, source)
1660        }
1661        "template_type" | "template_function" => node
1662            .child_by_field_name("name")
1663            .and_then(|name| recovered_malformed_base_name(name, source)),
1664        "ERROR" => None,
1665        "qualified_identifier" | "scoped_type_identifier" => {
1666            let suffix = node
1667                .child_by_field_name("name")
1668                .and_then(|name| recovered_malformed_base_name(name, source))?;
1669            let scope = node
1670                .child_by_field_name("scope")
1671                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1672            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1673                malformed_qualified_prefix(node, source)?
1674            } else {
1675                if malformed_qualified_prefix(node, source).is_some() {
1676                    return None;
1677                }
1678                scope
1679            };
1680            Some(format!("{prefix}::{suffix}"))
1681        }
1682        _ => None,
1683    }
1684}
1685
1686fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1687    let mut pending = vec![node];
1688    let mut components = Vec::new();
1689    while let Some(current) = pending.pop() {
1690        match current.kind() {
1691            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1692                components.push(recovered_base_atom(current, source)?);
1693            }
1694            "template_type" | "template_function" => {
1695                pending.push(current.child_by_field_name("name")?);
1696            }
1697            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1698                pending.push(current.child_by_field_name("name")?);
1699                if let Some(scope) = current.child_by_field_name("scope") {
1700                    // Malformed export bases can put the access keyword in
1701                    // `scope` and the real scope in the direct ERROR child.
1702                    // Keep the exact AST recovery used by the declaration.
1703                    if recovered_base_atom(scope, source).is_some_and(|name| {
1704                        matches!(name.as_str(), "public" | "protected" | "private")
1705                    }) {
1706                        pending.push(malformed_qualified_prefix_node(current, source)?);
1707                    } else {
1708                        if malformed_qualified_prefix_node(current, source).is_some() {
1709                            return None;
1710                        }
1711                        pending.push(scope);
1712                    }
1713                }
1714            }
1715            _ => return None,
1716        }
1717    }
1718    Some(components)
1719}
1720
1721fn recovered_base_specifier(
1722    node: Node<'_>,
1723    source: &str,
1724    is_virtual: bool,
1725) -> Option<CppBaseSpecifierFact> {
1726    let components = recovered_base_components(node, source)?;
1727    (!components.is_empty()).then_some(CppBaseSpecifierFact {
1728        absolute: node.child(0).is_some_and(|token| token.kind() == "::"),
1729        components,
1730        is_virtual,
1731    })
1732}
1733
1734fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1735    // Recovery can put the qualifier in an ERROR sibling or inside the base
1736    // node. Read only the AST prefix through this base, resetting at preceding
1737    // inheritance delimiters so another base's qualifier cannot leak here.
1738    let mut pending = vec![container];
1739    let mut is_virtual = false;
1740    while let Some(node) = pending.pop() {
1741        if node.start_byte() >= base.end_byte() {
1742            break;
1743        }
1744        if node.child_count() != 0 {
1745            let mut cursor = node.walk();
1746            let children = node
1747                .children(&mut cursor)
1748                .take_while(|child| child.start_byte() < base.end_byte())
1749                .collect::<Vec<_>>();
1750            pending.extend(children.into_iter().rev());
1751            continue;
1752        }
1753        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1754            is_virtual = false;
1755        } else if node.kind() == "virtual"
1756            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1757        {
1758            is_virtual = true;
1759        }
1760    }
1761    is_virtual
1762}
1763
1764fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1765    if !matches!(
1766        node.kind(),
1767        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1768    ) {
1769        return None;
1770    }
1771    let name = normalize_cpp_whitespace(node_text(node, source));
1772    (!name.is_empty()).then_some(name)
1773}
1774
1775fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1776    malformed_qualified_prefix_node(node, source)
1777        .and_then(|prefix| recovered_base_atom(prefix, source))
1778}
1779
1780fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1781    let mut prefix = None;
1782    let mut cursor = node.walk();
1783    for error in node
1784        .named_children(&mut cursor)
1785        .filter(|child| child.kind() == "ERROR")
1786    {
1787        if prefix.is_some() {
1788            return None;
1789        }
1790        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1791        // the qualified name's scope and leaves `virtual` beside the real
1792        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1793        // component of the base's name (#2924).
1794        let mut error_cursor = error.walk();
1795        let atoms = error
1796            .named_children(&mut error_cursor)
1797            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1798            .collect::<Option<Vec<_>>>()?;
1799        let [atom] = atoms
1800            .iter()
1801            .filter(|(_, name)| name != "virtual")
1802            .collect::<Vec<_>>()[..]
1803        else {
1804            return None;
1805        };
1806        prefix = Some(atom.0);
1807    }
1808    prefix
1809}
1810
1811type RecoveredFunctionDefinitionClass<'tree> = (
1812    Node<'tree>,
1813    String,
1814    Option<Vec<String>>,
1815    Option<Vec<CppBaseSpecifierFact>>,
1816);
1817/// One declaration an attribute-like macro invocation swallowed into a
1818/// declaration-scope `ERROR`, with the byte range that spells it.
1819/// What [`stranded_declaration_run`] read out of one node.
1820struct StrandedRun<'tree> {
1821    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1822    /// Whether every part of the node read as part of a declaration. False when
1823    /// a part the reader does not understand ended it early, or when the last
1824    /// parts were types with no declarator after them. Callers that index what
1825    /// was found keep the declarations either way; a caller deciding whether a
1826    /// whole region is safe to index requires this.
1827    complete: bool,
1828}
1829
1830struct MacroWrappedDeclaration<'tree> {
1831    declarator: Node<'tree>,
1832    range: Range,
1833    /// Whether the recovered declaration spells `static`. The envelope hides
1834    /// that keyword from the ordinary linkage reader, which looks for it among
1835    /// a declaration node's own children, and internal linkage is what decides
1836    /// whether a header declaration and a body in another file are one symbol.
1837    is_static: bool,
1838}
1839
1840/// Whether `node` stands where declarations live: directly in the translation
1841/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1842/// stands in one of those.
1843///
1844/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1845/// where it stands. The case that matters is a whole translation unit the
1846/// parser could not close: whisper.cpp's `include/whisper.h` opens
1847/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1848/// so the braces never balance in the token stream tree-sitter sees, the file's
1849/// own root node is an `ERROR`, and every top-level declaration in the file is
1850/// a child of it (#3094). Without this the macro recoveries below read nothing
1851/// in such a file, because none of their envelopes is in a declaration scope
1852/// the parser managed to build.
1853fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1854    declaration_scope_container(node, ancestry).is_some()
1855}
1856
1857/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1858/// end is where a parse failure that starts at `node` can still be doing
1859/// damage: a declaration scope has no synchronization point of its own, so the
1860/// parser carries such a failure to the scope's close.
1861fn declaration_scope_container<'tree>(
1862    node: Node<'tree>,
1863    ancestry: &ParentIndex<'tree>,
1864) -> Option<Node<'tree>> {
1865    let mut parent = ancestry.parent(node)?;
1866    loop {
1867        match parent.kind() {
1868            "translation_unit" => return Some(parent),
1869            "declaration_list" => {
1870                return ancestry
1871                    .parent(parent)
1872                    .is_some_and(|grandparent| {
1873                        matches!(
1874                            grandparent.kind(),
1875                            "namespace_definition" | "linkage_specification"
1876                        )
1877                    })
1878                    .then_some(parent);
1879            }
1880            "ERROR" => match ancestry.parent(parent) {
1881                Some(grandparent) => parent = grandparent,
1882                // A root `ERROR` is the translation unit the parser could not
1883                // build, so its children stand at file scope.
1884                None => return Some(parent),
1885            },
1886            _ => return None,
1887        }
1888    }
1889}
1890
1891/// Whether `node` is an `ERROR` the parser produced where declarations live.
1892fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1893    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1894}
1895
1896/// Whether `node` can only be part of what precedes a declarator -- a type, a
1897/// specifier, or a word of the attribute macro's own text that the lexer left
1898/// as a bare identifier -- so a run of these followed by a declarator is one
1899/// declaration the parser failed to group.
1900fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1901    matches!(
1902        node.kind(),
1903        "identifier"
1904            | "type_identifier"
1905            | "primitive_type"
1906            | "sized_type_specifier"
1907            | "struct_specifier"
1908            | "union_specifier"
1909            | "enum_specifier"
1910            | "type_qualifier"
1911            | "storage_class_specifier"
1912            | "explicit_function_specifier"
1913            | "virtual_function_specifier"
1914            | "qualified_identifier"
1915            | "template_type"
1916            | "dependent_type"
1917            | "placeholder_type_specifier"
1918    )
1919}
1920
1921/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1922/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1923/// words the lexer reports as bare identifiers. Anything else in such an
1924/// `ERROR` stops the recovery, so a macro argument that carries structure this
1925/// recovery does not understand is never guessed at.
1926fn is_macro_argument_error(node: Node<'_>) -> bool {
1927    if node.kind() != "ERROR" {
1928        return false;
1929    }
1930    let mut cursor = node.walk();
1931    node.named_children(&mut cursor).all(|child| {
1932        matches!(
1933            child.kind(),
1934            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1935        )
1936    })
1937}
1938
1939/// The end of a recovered declaration: the declarator's end, extended across
1940/// the `;` the grammar left beside it inside the envelope.
1941///
1942/// The envelope's own end is the hard boundary. The parser hands the last
1943/// declaration's `;` to the sibling statement it recovered with, outside the
1944/// envelope, and a range that reached it would no longer lie inside one node --
1945/// which is how every reader (including the resolver's climb from a range to
1946/// the node that declares it) finds a recovered declaration again.
1947fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1948    declarator
1949        .next_sibling()
1950        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1951        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1952}
1953
1954/// The declarations `node` holds after an attribute-like macro cost the parser
1955/// their grouping, in source order.
1956///
1957/// The parser packs the parts into whichever slots it has left. In whisper's
1958/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1959/// field takes the export macro, the real return type and the *next*
1960/// declaration's declarator both end up inside one sibling `ERROR`, and the
1961/// `declarator` field takes whatever declaration the recovery reached last. In
1962/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1963/// arrive as bare identifiers and the attributed member and the member after it
1964/// share one declarator node.
1965///
1966/// Both are the same failure, so both get the same reading: flatten the node's
1967/// parts -- splicing each nested `ERROR`'s own children in place, since an
1968/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1969/// spells, a run of type-and-specifier tokens followed by a declarator, over
1970/// and over.
1971///
1972/// Fails closed. A part that is neither a declarator nor something that can
1973/// only precede one ends the recovery there, and the declarations before it are
1974/// kept.
1975fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1976    let mut parts = Vec::new();
1977    let mut cursor = node.walk();
1978    for child in node.named_children(&mut cursor) {
1979        if child.kind() == "ERROR" {
1980            let mut error_cursor = child.walk();
1981            parts.extend(child.named_children(&mut error_cursor));
1982        } else {
1983            parts.push(child);
1984        }
1985    }
1986
1987    let mut declarations = Vec::new();
1988    let mut start = None;
1989    let mut is_static = false;
1990    let mut complete = true;
1991    for part in parts {
1992        if part.kind() == "comment" {
1993            continue;
1994        }
1995        if let Some(declarator) = extract_function_declarator(part) {
1996            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1997            declarations.push(MacroWrappedDeclaration {
1998                declarator,
1999                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2000                is_static,
2001            });
2002            is_static = false;
2003            continue;
2004        }
2005        if !is_recovered_declaration_type_part(part) {
2006            complete = false;
2007            break;
2008        }
2009        is_static |= part.kind() == "storage_class_specifier"
2010            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2011        start.get_or_insert(part.start_byte());
2012    }
2013    StrandedRun {
2014        declarations,
2015        complete: complete && start.is_none(),
2016    }
2017}
2018
2019/// The declarations an attribute-like macro invocation swallowed into a
2020/// declaration-scope `ERROR`, in source order.
2021///
2022/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2023/// declaration in a macro call:
2024///
2025/// ```text
2026/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2027///                  struct llama_model * model,
2028///           struct llama_context_params   params),
2029///         "use llama_init_from_model instead");
2030/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2031/// ```
2032///
2033/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2034/// holding the macro name, the wrapped declaration as a
2035/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2036/// following declaration as a further `parameter_declaration` until it
2037/// recovers. That is what removed `llama_tokenize` from the index and left the
2038/// seven-argument call in `talk-llama.cpp` with only that file's own
2039/// three-parameter `static` overload to choose from (#2552, and the
2040/// `LLAMA_API` half of #2551).
2041///
2042/// Every part of every swallowed declaration is still a real node; only their
2043/// grouping is lost. This rebuilds the grouping and reads the nodes.
2044fn macro_wrapped_declarations<'tree>(
2045    envelope: Node<'tree>,
2046    source: &str,
2047    ancestry: &ParentIndex<'tree>,
2048) -> Vec<MacroWrappedDeclaration<'tree>> {
2049    let mut declarations = Vec::new();
2050    if !is_declaration_scope_error(envelope, ancestry) {
2051        return declarations;
2052    }
2053    let mut cursor = envelope.walk();
2054    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2055    let [macro_name, arguments @ ..] = children.as_slice() else {
2056        return declarations;
2057    };
2058    if macro_name.kind() != "identifier" {
2059        return declarations;
2060    }
2061    let mut wrapped_declaration_seen = false;
2062    for argument in arguments {
2063        match argument.kind() {
2064            "comment" => {}
2065            "parameter_declaration" => {
2066                let recovered = stranded_declaration_run(*argument, source).declarations;
2067                if recovered.is_empty() {
2068                    break;
2069                }
2070                wrapped_declaration_seen = true;
2071                declarations.extend(recovered);
2072            }
2073            // The hint string, and only that: an argument the recovery cannot
2074            // read as a declaration is admitted before the wrapped declaration
2075            // is found, so a macro whose first argument is not a declaration
2076            // recovers nothing.
2077            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
2078            _ => break,
2079        }
2080    }
2081    declarations
2082}
2083
2084/// What one macro invocation swallowed when it collapsed a whole run of
2085/// declarations.
2086struct CollapsedMacroDeclarationRun {
2087    /// The byte just past the `;` that closes the invocation, which is where
2088    /// the declarations it swallowed begin.
2089    invocation_end: usize,
2090    /// The byte this recovery owns to: the close of the enclosing declaration
2091    /// scope when the invocation swallowed the declarations written after it,
2092    /// and the invocation's own end when the parser instead spread the
2093    /// invocation itself across the container's children and swallowed nothing.
2094    region_end: usize,
2095}
2096
2097/// The tokens one collapsed macro invocation spans, in source order: the leaves
2098/// of `node`, then the leaves of the siblings written after it. Comments and
2099/// the parser's own MISSING tokens are skipped; neither is in the source the
2100/// invocation is written in.
2101///
2102/// The parser puts the parts of a failed invocation wherever it has room. It
2103/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2104/// `expression_statement` it recovered with; it spreads them flat over the
2105/// container's children; it packs the tail of the file into one of the
2106/// arguments. The grouping therefore says nothing, but the token order still
2107/// spells the invocation, which is what this reads.
2108struct MacroInvocationTokens<'tree> {
2109    stack: Vec<Node<'tree>>,
2110    /// The last sibling handed to `stack`, and the point the next one is asked
2111    /// for. Held as the node rather than as its already-computed successor so
2112    /// the successor is asked for only when the walk actually runs off the end
2113    /// of what it has: `Node::next_sibling` recovers the parent first, which
2114    /// re-descends from the root, so it costs the node's position in the tree.
2115    /// Nearly every node this iterator is built for is rejected on its first
2116    /// token, before any sibling is needed, and computing one eagerly made that
2117    /// rejection cost a walk of the whole container (capstone's generated
2118    /// instruction tables, t19).
2119    frontier: Option<Node<'tree>>,
2120}
2121
2122impl<'tree> MacroInvocationTokens<'tree> {
2123    fn new(node: Node<'tree>) -> Self {
2124        Self {
2125            stack: vec![node],
2126            frontier: Some(node),
2127        }
2128    }
2129}
2130
2131impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2132    type Item = Node<'tree>;
2133
2134    fn next(&mut self) -> Option<Node<'tree>> {
2135        loop {
2136            let Some(node) = self.stack.pop() else {
2137                let sibling = self.frontier?.next_sibling()?;
2138                self.frontier = Some(sibling);
2139                self.stack.push(sibling);
2140                continue;
2141            };
2142            if node.child_count() == 0 {
2143                if node.kind() == "comment" || node.is_missing() {
2144                    continue;
2145                }
2146                return Some(node);
2147            }
2148            let mut cursor = node.walk();
2149            let children = node.children(&mut cursor).collect::<Vec<_>>();
2150            self.stack.extend(children.into_iter().rev());
2151        }
2152    }
2153}
2154
2155/// The macro invocation that begins at `node` when the parser could not group
2156/// it, or `None` when `node` does not begin one.
2157///
2158/// whisper.cpp's bundled `llama.h` writes
2159///
2160/// ```text
2161/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2162///                          const char * path_model,
2163///           struct llama_model_params   params),
2164///         "use llama_model_load_from_file instead");
2165/// ```
2166///
2167/// and its own `include/whisper.h` writes
2168///
2169/// ```text
2170/// WHISPER_DEPRECATED(
2171///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2172///     "use whisper_init_from_file_with_params instead"
2173/// );
2174/// ```
2175///
2176/// tree-sitter cannot know either name is a macro, so it reads the name and its
2177/// `(` as a function declarator whose close it never finds, and carries that
2178/// failure forward over the declarations written after it. In `llama.h` that
2179/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2180/// (#2551). In `whisper.h` it instead flattens the invocations over the
2181/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2182/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2183/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2184/// `function_definition` when a brace block falls in a swallowed tail, because
2185/// the parser borrows it for the body the bogus definition needs, and an
2186/// `ERROR` or a bare token when none does. None of that says anything about the
2187/// construct, so the node's kind is not the criterion; the token order is.
2188///
2189/// The invocation ends at the `)` that closes its own `(`, immediately followed
2190/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2191/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2192/// its own, so a `;` reached before the close means this is some other
2193/// construct -- a macro-defined function body, say -- and the ordinary readers
2194/// keep the node.
2195///
2196/// Fails closed everywhere else too. The head must be an export-macro-shaped
2197/// token followed by `(`, the node must stand where declarations live, and an
2198/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2199/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2200/// nothing past it -- is left to that reader.
2201fn collapsed_macro_declaration_run<'tree>(
2202    node: Node<'tree>,
2203    source: &str,
2204    ancestry: &ParentIndex<'tree>,
2205) -> Option<CollapsedMacroDeclarationRun> {
2206    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2207    let mut tokens = MacroInvocationTokens::new(node);
2208    let name = tokens.next()?;
2209    if !matches!(name.kind(), "identifier" | "type_identifier")
2210        || !cpp_export_macro_token(node_text(name, source))
2211    {
2212        return None;
2213    }
2214    if tokens.next()?.kind() != "(" {
2215        return None;
2216    }
2217    let mut depth = 1usize;
2218    let invocation_end = loop {
2219        let token = tokens.next()?;
2220        match token.kind() {
2221            "(" => depth += 1,
2222            ";" => return None,
2223            ")" => {
2224                depth -= 1;
2225                if depth == 0 {
2226                    let semicolon = tokens.next()?;
2227                    if semicolon.kind() != ";" {
2228                        return None;
2229                    }
2230                    break semicolon.end_byte();
2231                }
2232            }
2233            _ => {}
2234        }
2235    };
2236    // An invocation that reaches the end of its own declaration scope swallowed
2237    // nothing: there is nothing after it left to recover, and the ordinary
2238    // readers have the invocation itself. This is also what stops the scan
2239    // below from re-entering itself, since the region it reparses for one
2240    // invocation ends exactly at that invocation's `;`.
2241    if invocation_end >= container_end {
2242        return None;
2243    }
2244    // Where the damage ends. A node that reaches past its own invocation packed
2245    // real declarations into the invocation's argument list, so the parser was
2246    // still failing when it got there and everything to the close of the scope
2247    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2248    // line 455 with it, and every declaration from there to the end of the file
2249    // is shredded. A node that stops at or before the invocation's `;` holds
2250    // nothing but the invocation, whose own parts the parser spread over the
2251    // siblings after it.
2252    let region_end = if node.end_byte() > invocation_end {
2253        container_end
2254    } else {
2255        invocation_end
2256    };
2257    Some(CollapsedMacroDeclarationRun {
2258        invocation_end,
2259        region_end,
2260    })
2261}
2262
2263/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2264/// ordinary class body, and the members it swallowed.
2265///
2266/// Botan deprecates members that way:
2267///
2268/// ```text
2269/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2270/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2271/// ```
2272///
2273/// The parser reads the macro name as the member's type and its argument list
2274/// as a parenthesized declarator, which then swallows the attributed member
2275/// *and* the member written after it: a `field_declaration` whose `type` is a
2276/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2277/// opening with an `ERROR` whose first part is a bare identifier -- the first
2278/// word of the string, which the lexer could not keep together.
2279///
2280/// The macro's spelling is not the criterion; that structural shape is. The
2281/// returned declarations are in source order, the first being the attributed
2282/// member and the rest the members the declarator swallowed after it.
2283fn string_attribute_macro_member_declarators<'tree>(
2284    field: Node<'tree>,
2285    source: &str,
2286) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2287    if field.kind() != "field_declaration"
2288        || field
2289            .child_by_field_name("type")
2290            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2291    {
2292        return None;
2293    }
2294    let declarator = field.child_by_field_name("declarator")?;
2295    if declarator.kind() != "parenthesized_declarator" {
2296        return None;
2297    }
2298    let opening = declarator.named_child(0)?;
2299    if opening.kind() != "ERROR"
2300        || opening
2301            .named_child(0)
2302            .is_none_or(|word| word.kind() != "identifier")
2303    {
2304        return None;
2305    }
2306    let declarations = stranded_declaration_run(declarator, source).declarations;
2307    (!declarations.is_empty()).then_some(declarations)
2308}
2309
2310/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2311/// export-macro class body leaves in front of the members that macro decorated.
2312///
2313/// The reparse reads the class body as statements, so the attribute becomes a
2314/// call statement of its own -- with the `;` the grammar had to invent -- and
2315/// the members after it are stranded in the `ERROR` that follows.
2316fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2317    let Some(call) = (node.kind() == "expression_statement")
2318        .then(|| node.named_child(0))
2319        .flatten()
2320        .filter(|child| child.kind() == "call_expression")
2321    else {
2322        return false;
2323    };
2324    call.child_by_field_name("function")
2325        .is_some_and(|function| function.kind() == "identifier")
2326        && call
2327            .child_by_field_name("arguments")
2328            .is_some_and(|arguments| {
2329                let mut cursor = arguments.walk();
2330                arguments.named_child_count() > 0
2331                    && arguments
2332                        .named_children(&mut cursor)
2333                        .all(|argument| argument.kind() == "string_literal")
2334            })
2335}
2336
2337/// The start byte of the call the region reparse left where an access-labeled
2338/// constructor was written.
2339///
2340/// A constructor is a member only inside a class body. The export-macro class
2341/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2342/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2343/// comma-expression under `private:`, with no declarator left in the tree. It
2344/// is still spelled in the source, though, starting at the one call under the
2345/// label whose callee is the class's own name. Botan's private six-parameter
2346/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2347/// call is an ambiguity this declines, and so is a reparse from that byte that
2348/// does not yield exactly one constructor declarator there.
2349fn cpp_access_label_constructor_call_start(
2350    node: Node<'_>,
2351    class_name: &str,
2352    source: &str,
2353) -> Option<usize> {
2354    if node.kind() != "labeled_statement" {
2355        return None;
2356    }
2357    let label = node.named_child(0)?;
2358    if label.kind() != "statement_identifier"
2359        || !matches!(
2360            node_text(label, source).trim(),
2361            "public" | "private" | "protected"
2362        )
2363    {
2364        return None;
2365    }
2366    let mut starts = Vec::new();
2367    let mut stack = vec![node];
2368    while let Some(current) = stack.pop() {
2369        if current.kind() == "call_expression"
2370            && current
2371                .child_by_field_name("function")
2372                .is_some_and(|function| {
2373                    function.kind() == "identifier"
2374                        && node_text(function, source).trim() == class_name
2375                })
2376        {
2377            starts.push(current.start_byte());
2378        }
2379        let mut cursor = current.walk();
2380        stack.extend(current.named_children(&mut cursor));
2381    }
2382    let [start] = starts.as_slice() else {
2383        return None;
2384    };
2385    Some(*start)
2386}
2387
2388/// The `function_definition` that gives `declarator` a body, following only the
2389/// declarator chain, so a recovered callable knows whether it is a declaration
2390/// or a definition without anyone having to say.
2391fn cpp_declarator_function_definition<'tree>(
2392    declarator: Node<'tree>,
2393    ancestry: &ParentIndex<'tree>,
2394) -> Option<Node<'tree>> {
2395    let mut current = declarator;
2396    while let Some(parent) = ancestry.parent(current) {
2397        match parent.kind() {
2398            "function_definition" if parent.child_by_field_name("body").is_some() => {
2399                return Some(parent);
2400            }
2401            "pointer_declarator"
2402            | "reference_declarator"
2403            | "parenthesized_declarator"
2404            | "array_declarator" => current = parent,
2405            _ => return None,
2406        }
2407    }
2408    None
2409}
2410
2411/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2412/// declaration walk still reaches, so a recovery that runs ahead of that walk
2413/// would name what it finds without the namespace.
2414fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2415    let mut current = node;
2416    while let Some(parent) = ancestry.parent(current) {
2417        if parent.kind() == "namespace_definition"
2418            && parent.child_by_field_name("body") == Some(current)
2419        {
2420            return true;
2421        }
2422        current = parent;
2423    }
2424    false
2425}
2426
2427/// Whether the source a recovered callable's byte range spells is a definition
2428/// (a declarator with a body) rather than a declaration.
2429///
2430/// A callable recovered from a mangled region owns no node of its own in the
2431/// file's tree, so the occurrence-role scan that climbs from the range to the
2432/// node containing it lands on whatever container the parser left and answers
2433/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2434/// constructor a declaration and left its call site with nothing to navigate to
2435/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2436/// extraction used, and read the answer from the one declaration it spells.
2437///
2438/// `None` when the range is not one recovered declaration on its own, which is
2439/// the case for every ordinary declaration, so callers keep their own answer.
2440pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2441    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2442    let root = tree.root_node();
2443    let mut cursor = root.walk();
2444    let items = root
2445        .named_children(&mut cursor)
2446        .filter(|child| child.kind() != "comment")
2447        .collect::<Vec<_>>();
2448    let [item] = items.as_slice() else {
2449        return None;
2450    };
2451    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2452        return None;
2453    }
2454    match item.kind() {
2455        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2456        "declaration" | "field_declaration" => Some(false),
2457        _ => None,
2458    }
2459}
2460
2461/// Whether `node` is an envelope an attribute-like macro invocation left where
2462/// declarations were written: the declaration-scope `ERROR`
2463/// [`macro_wrapped_declarations`] reads, or the collapsed run
2464/// [`collapsed_macro_declaration_run`] reads.
2465///
2466/// Extraction and resolution must read one definition of this shape. The
2467/// resolver climbs from a declaration's recorded byte range to the node that
2468/// declares it, and a recovered declaration's range lies inside one of these
2469/// envelopes rather than inside a `declaration` node, so the climb stops here
2470/// (the same role `is_recovered_exported_class_container` plays for a recovered
2471/// class).
2472pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2473    let ancestry = ParentIndex::unindexed();
2474    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2475        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2476}
2477
2478fn recover_exported_class_function_definition<'tree>(
2479    node: Node<'tree>,
2480    source: &str,
2481) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2482    if node.kind() != "function_definition" {
2483        return None;
2484    }
2485    if let Some(prefix) = node.prev_named_sibling()
2486        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2487        && recovered.range.end_byte == node.end_byte()
2488    {
2489        return Some((
2490            node,
2491            recovered.name,
2492            recovered.raw_supertypes,
2493            recovered.base_facts,
2494        ));
2495    }
2496    let type_node = node.child_by_field_name("type")?;
2497    let declarator = node.child_by_field_name("declarator")?;
2498
2499    if matches!(
2500        type_node.kind(),
2501        "class_specifier" | "struct_specifier" | "union_specifier"
2502    ) {
2503        let type_name = type_node
2504            .child_by_field_name("name")
2505            .and_then(|name| direct_identifier_name(name, source));
2506        let exported_macro_type = type_name
2507            .as_ref()
2508            .is_some_and(|name| cpp_export_macro_token(name));
2509        if exported_macro_type {
2510            let mut cursor = node.walk();
2511            let errors_before_declarator = node
2512                .named_children(&mut cursor)
2513                .filter(|child| {
2514                    child.kind() == "ERROR"
2515                        && child.start_byte() >= type_node.end_byte()
2516                        && child.end_byte() <= declarator.start_byte()
2517                })
2518                .collect::<Vec<_>>();
2519            if let Some(name) = errors_before_declarator
2520                .iter()
2521                .find_map(|error| displaced_exported_class_name(*error, source))
2522            {
2523                let base_node = errors_before_declarator
2524                    .iter()
2525                    .any(|error| malformed_inheritance_syntax(*error))
2526                    .then_some(declarator);
2527                let raw_supertypes = base_node
2528                    .and_then(|base| recovered_malformed_base_name(base, source))
2529                    .map(|base| vec![base]);
2530                let base_facts = base_node
2531                    .and_then(|base| {
2532                        recovered_base_specifier(
2533                            base,
2534                            source,
2535                            recovered_base_is_virtual(node, base, source),
2536                        )
2537                    })
2538                    .map(|base| vec![base]);
2539                return Some((node, name, raw_supertypes, base_facts));
2540            }
2541            if errors_before_declarator
2542                .iter()
2543                .any(|error| malformed_inheritance_syntax(*error))
2544            {
2545                return None;
2546            }
2547        }
2548        if !exported_macro_type
2549            && let Some(name) = type_name
2550            && !cpp_export_macro_token(&name)
2551            && let Some(base) =
2552                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2553        {
2554            let base_node =
2555                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2556            let base_facts = Some(vec![recovered_base_specifier(
2557                base_node,
2558                source,
2559                recovered_base_is_virtual(node, base_node, source),
2560            )?]);
2561            return Some((node, name, Some(vec![base]), base_facts));
2562        }
2563        if let Some(name) = direct_identifier_name(declarator, source)
2564            && exported_macro_type
2565            && !cpp_export_macro_token(&name)
2566        {
2567            let base_node = exported_macro_type
2568                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2569                .flatten();
2570            let raw_supertypes = base_node
2571                .and_then(|base| recovered_malformed_base_name(base, source))
2572                .map(|base| vec![base]);
2573            let base_facts = base_node
2574                .and_then(|base| {
2575                    recovered_base_specifier(
2576                        base,
2577                        source,
2578                        recovered_base_is_virtual(node, base, source),
2579                    )
2580                })
2581                .map(|base| vec![base]);
2582            return Some((node, name, raw_supertypes, base_facts));
2583        }
2584        if declarator.kind() == "parenthesized_declarator"
2585            && type_node
2586                .child_by_field_name("name")
2587                .and_then(|name| direct_identifier_name(name, source))
2588                .is_some_and(|name| cpp_export_macro_token(&name))
2589        {
2590            if let Some((name, base_node)) =
2591                recovered_function_like_export_class_owner_node(declarator, source)
2592            {
2593                return Some((
2594                    node,
2595                    name,
2596                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2597                    Some(vec![recovered_base_specifier(
2598                        base_node,
2599                        source,
2600                        recovered_base_is_virtual(node, base_node, source),
2601                    )?]),
2602                ));
2603            }
2604            let body_start = node
2605                .child_by_field_name("body")
2606                .map(|body| body.start_byte())
2607                .unwrap_or(node.end_byte());
2608            let mut cursor = node.walk();
2609            if let Some(name) = node
2610                .named_children(&mut cursor)
2611                .filter(|child| {
2612                    child.kind() == "ERROR"
2613                        && child.start_byte() >= declarator.end_byte()
2614                        && child.end_byte() <= body_start
2615                })
2616                .find_map(|error| declarator_name_from_node(error, source))
2617            {
2618                return Some((node, name, None, None));
2619            }
2620        }
2621    }
2622
2623    let declarator_text = direct_identifier_name(declarator, source)?;
2624    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2625        return None;
2626    }
2627    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2628}
2629
2630fn recovered_function_like_export_class_owner_node<'tree>(
2631    declarator: Node<'tree>,
2632    source: &str,
2633) -> Option<(String, Node<'tree>)> {
2634    if declarator.kind() != "parenthesized_declarator" {
2635        return None;
2636    }
2637    let mut cursor = declarator.walk();
2638    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2639    let [prefix, base] = children.as_slice() else {
2640        return None;
2641    };
2642    if prefix.kind() != "ERROR"
2643        || !matches!(
2644            base.kind(),
2645            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2646        )
2647    {
2648        return None;
2649    }
2650    let mut identifiers = Vec::new();
2651    let mut prefix_cursor = prefix.walk();
2652    for child in prefix.named_children(&mut prefix_cursor) {
2653        match child.kind() {
2654            "number_literal" | "string_literal" | "char_literal" => {}
2655            "identifier" | "type_identifier" => {
2656                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2657            }
2658            _ => return None,
2659        }
2660    }
2661    let name = match identifiers.as_slice() {
2662        [name] => name.clone(),
2663        [name, final_token] if final_token == "final" => name.clone(),
2664        _ => return None,
2665    };
2666    if name.is_empty() || cpp_export_macro_token(&name) {
2667        return None;
2668    }
2669    Some((name, *base))
2670}
2671
2672/// Collect the base names and source facts tree-sitter scattered across the
2673/// recovered head of a function-like export-macro class. A base arrives either
2674/// as a direct sibling identifier or inside the `ERROR` node the grammar
2675/// produced for a `: public Base` fragment. The grammar leaves the `final`
2676/// specifier and access specifiers in the same position as the bases, so drop
2677/// them.
2678/// The bases a recovered function-like export-macro class head names between
2679/// `after` (the end of the class name, or of the access specifier that stands
2680/// in for it) and `before` (the start of the body, or of the `init_declarator`
2681/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2682/// fragments, and one fragment can also hold the class name and `final`
2683/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2684/// than by the fragment that holds it.
2685fn recovered_export_head_base_data(
2686    node: Node<'_>,
2687    after: usize,
2688    before: usize,
2689    source: &str,
2690) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
2691    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2692    let mut base_nodes = Vec::new();
2693    let mut cursor = node.walk();
2694    for child in node.named_children(&mut cursor) {
2695        if child.kind() == "ERROR" {
2696            let mut error_cursor = child.walk();
2697            base_nodes.extend(
2698                child
2699                    .named_children(&mut error_cursor)
2700                    .filter(within)
2701                    .filter_map(|part| {
2702                        recovered_malformed_base_name(part, source).map(|_| (part, child))
2703                    }),
2704            );
2705        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
2706            base_nodes.push((child, node));
2707        }
2708    }
2709    let mut bases = Vec::new();
2710    let mut facts = Vec::new();
2711    for (base, container) in base_nodes {
2712        let Some(name) = recovered_malformed_base_name(base, source) else {
2713            continue;
2714        };
2715        if matches!(
2716            name.as_str(),
2717            "final" | "public" | "protected" | "private" | "virtual"
2718        ) {
2719            continue;
2720        }
2721        bases.push(name);
2722        if let Some(fact) = recovered_base_specifier(
2723            base,
2724            source,
2725            recovered_base_is_virtual(container, base, source),
2726        ) {
2727            facts.push(fact);
2728        }
2729    }
2730    (bases, facts)
2731}
2732
2733fn recovered_export_head_bases(
2734    node: Node<'_>,
2735    after: usize,
2736    before: usize,
2737    source: &str,
2738) -> Vec<String> {
2739    recovered_export_head_base_data(node, after, before, source).0
2740}
2741
2742/// Whether `token` is the `final` that closes a recovered class head. The
2743/// grammar keeps it as the `final` keyword when it recognised the head
2744/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2745/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2746fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2747    if token.is_named() {
2748        token.kind() == "identifier" && node_text(token, source) == "final"
2749    } else {
2750        token.kind() == "final"
2751    }
2752}
2753
2754/// The class name of a recovered function-like export-macro class head, read by
2755/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2756/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2757/// carries the body. The head's tokens, in source order with `ERROR` fragments
2758/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2759/// identifier before the head ends, and the head ends at `final`, at the base
2760/// clause `:`, or at `tail`. Every identifier before the name is decoration
2761/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2762/// (`X509_CA`) is a class name like any other.
2763///
2764/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2765/// rule, not a token of the source, so it is never the name.
2766fn recovered_export_head_name<'tree>(
2767    node: Node<'tree>,
2768    tail: Node<'tree>,
2769    source: &str,
2770) -> Option<Node<'tree>> {
2771    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2772}
2773
2774/// The class name of a head whose tokens straddle the recovered pair: the
2775/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2776/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2777/// The positional rule is the same, read over the two token runs in source
2778/// order, so a name in either node wins by position rather than by which node
2779/// holds it.
2780fn recovered_export_pair_head_name<'tree>(
2781    prefix: Node<'tree>,
2782    sibling: Node<'tree>,
2783    tail: Node<'tree>,
2784    source: &str,
2785) -> Option<Node<'tree>> {
2786    let mut tokens = export_head_tokens(prefix, None);
2787    tokens.extend(export_head_tokens(sibling, Some(tail)));
2788    export_head_name_from_tokens(&tokens, source)
2789}
2790
2791/// One node's head tokens in source order, with `ERROR` fragments spliced in
2792/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2793/// before `tail` when the caller names one.
2794fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2795    let mut tokens = Vec::new();
2796    let mut cursor = node.walk();
2797    for child in node.children(&mut cursor) {
2798        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2799            break;
2800        }
2801        if child.kind() == "ERROR" {
2802            let mut fragment_cursor = child.walk();
2803            tokens.extend(child.children(&mut fragment_cursor));
2804        } else {
2805            tokens.push(child);
2806        }
2807    }
2808    tokens
2809}
2810
2811/// The last identifier before the head ends, per the positional rule.
2812fn export_head_name_from_tokens<'tree>(
2813    tokens: &[Node<'tree>],
2814    source: &str,
2815) -> Option<Node<'tree>> {
2816    let mut name = None;
2817    for token in tokens.iter().copied() {
2818        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2819        {
2820            break;
2821        }
2822        if token.is_named()
2823            && !token.is_missing()
2824            && matches!(
2825                token.kind(),
2826                "identifier" | "type_identifier" | "field_identifier"
2827            )
2828        {
2829            name = Some(token);
2830        }
2831    }
2832    name
2833}
2834
2835/// The `init_declarator` whose `value` carries the class body when the grammar
2836/// keeps a function-like export-macro class head as one `declaration`.
2837fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2838    let mut cursor = declaration.walk();
2839    declaration
2840        .named_children(&mut cursor)
2841        .find(|child| child.kind() == "init_declarator")
2842}
2843
2844/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2845/// of a function-like export-macro class head. The grammar splits a base list
2846/// across bare declarator fields, `ERROR` fragments, and one trailing
2847/// `init_declarator` whose `value` is the class body. `head_end` is where the
2848/// class identity ends and the bases can begin: the end of the access specifier
2849/// when the class name went to a statement label, and the end of the class name
2850/// itself otherwise.
2851fn recovered_export_declaration_tail<'tree>(
2852    declaration: Node<'tree>,
2853    head_end: usize,
2854    source: &str,
2855) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
2856    let init = recovered_export_init_declarator(declaration)?;
2857    let body = init.child_by_field_name("value")?;
2858    if body.kind() != "initializer_list" {
2859        return None;
2860    }
2861    let (mut bases, mut facts) =
2862        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
2863    let (more_bases, more_facts) =
2864        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
2865    bases.extend(more_bases);
2866    facts.extend(more_facts);
2867    Some((bases, (!facts.is_empty()).then_some(facts), body))
2868}
2869
2870/// Whether `node` is the head fragment tree-sitter leaves where
2871/// `class MACRO(args)` was written: the class keyword and the export macro's
2872/// function-like invocation in that order, with nothing else between them.
2873/// Spelling plays no part -- the invocation's `(` following the macro
2874/// identifier directly is what makes it function-like (#2557).
2875///
2876/// Two reductions produce the head. On its own the grammar keeps a body-less
2877/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2878/// with the invocation's `(`, arguments and `)` beside it. After object-like
2879/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2880/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2881/// `declaration` whose class keyword is demoted to a bare `identifier` and
2882/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2883fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2884    match node.kind() {
2885        "ERROR" => {
2886            let Some(class_node) = first_class_like_child(node) else {
2887                return false;
2888            };
2889            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2890                return false;
2891            }
2892            if class_node
2893                .child_by_field_name("name")
2894                .and_then(|name| direct_identifier_name(name, source))
2895                .is_none()
2896            {
2897                return false;
2898            }
2899            class_node
2900                .next_sibling()
2901                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2902        }
2903        "declaration" => {
2904            let mut cursor = node.walk();
2905            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2906            // The class keyword lost its own node: it survives as a bare
2907            // identifier spelled `class`, `struct` or `union`, which is never a
2908            // declarator or a type in well-formed code. It can sit inside an
2909            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2910            // a grouping failure rather than a construct, so read through it.
2911            let Some(keyword) = children
2912                .iter()
2913                .copied()
2914                .flat_map(|child| {
2915                    let mut cursor = child.walk();
2916                    if child.kind() == "ERROR" {
2917                        child.named_children(&mut cursor).collect::<Vec<_>>()
2918                    } else {
2919                        vec![child]
2920                    }
2921                })
2922                .find(|child| {
2923                    child.kind() == "identifier"
2924                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2925                })
2926            else {
2927                return false;
2928            };
2929            children.last().is_some_and(|init| {
2930                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2931                    let mut cursor = init.walk();
2932                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2933                    matches!(parts.as_slice(), [macro_name, arguments]
2934                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2935                            && arguments.kind() == "argument_list")
2936                }
2937            })
2938        }
2939        _ => false,
2940    }
2941}
2942
2943fn recover_function_like_export_class_pair(
2944    node: Node<'_>,
2945    source: &str,
2946) -> Option<RecoveredFunctionLikeExportClassPair> {
2947    if !is_function_like_export_class_head(node, source) {
2948        return None;
2949    }
2950    let sibling = node.next_named_sibling()?;
2951    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
2952        // At translation-unit scope tree-sitter can leave the malformed class
2953        // head in one ERROR node and parse its body as the adjacent compound
2954        // statement. The head still carries the export invocation, displaced
2955        // class name, and optional `final` token in source order, so recover
2956        // the name by the same positional rule as the other shapes.
2957        "compound_statement" => (
2958            recovered_export_pair_head_name(node, sibling, sibling, source)
2959                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2960            None,
2961            None,
2962            sibling,
2963        ),
2964        "expression_statement" => {
2965            let compound = sibling.named_child(0)?;
2966            if compound.kind() != "compound_literal_expression" {
2967                return None;
2968            }
2969            let body = compound.child_by_field_name("value")?;
2970            if body.kind() != "initializer_list" {
2971                return None;
2972            }
2973            (
2974                compound
2975                    .child_by_field_name("type")
2976                    .and_then(|name| direct_identifier_name(name, source))?,
2977                None,
2978                None,
2979                body,
2980            )
2981        }
2982        "labeled_statement" => {
2983            let label = sibling.child_by_field_name("label")?;
2984            if label.kind() != "statement_identifier" {
2985                return None;
2986            }
2987            let name = normalize_cpp_whitespace(node_text(label, source));
2988            let declaration = sibling
2989                .named_children(&mut sibling.walk())
2990                .find(|child| child.kind() == "declaration")?;
2991            let access = declaration.child_by_field_name("type")?;
2992            if !matches!(
2993                node_text(access, source),
2994                "public" | "protected" | "private"
2995            ) {
2996                return None;
2997            }
2998            let (bases, base_facts, body) =
2999                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3000            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3001        }
3002        // `class MACRO(2, 0) Name final { ... };`,
3003        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3004        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3005        // spread over the `type` field, the declarator field, and `ERROR`
3006        // fragments beside them; the name is the last one before the head ends.
3007        "function_definition" => {
3008            let body = sibling.child_by_field_name("body")?;
3009            if body.kind() != "compound_statement" {
3010                return None;
3011            }
3012            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3013            let (bases, base_facts) = recovered_export_head_base_data(
3014                sibling,
3015                name_node.end_byte(),
3016                body.start_byte(),
3017                source,
3018            );
3019            (
3020                normalize_cpp_whitespace(node_text(name_node, source)),
3021                (!bases.is_empty()).then_some(bases),
3022                (!base_facts.is_empty()).then_some(base_facts),
3023                body,
3024            )
3025        }
3026        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3027        // comma-separated base list makes the grammar keep the whole tail as one
3028        // declaration whose trailing `init_declarator` carries the body.
3029        "declaration" => {
3030            let init = recovered_export_init_declarator(sibling)?;
3031            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3032            let (bases, base_facts, body) =
3033                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3034            (
3035                normalize_cpp_whitespace(node_text(name_node, source)),
3036                (!bases.is_empty()).then_some(bases),
3037                base_facts,
3038                body,
3039            )
3040        }
3041        _ => return None,
3042    };
3043    debug_assert!(
3044        !name.is_empty(),
3045        "a recovered class head names its class by an identifier token"
3046    );
3047    let range = Range {
3048        start_byte: node.start_byte(),
3049        end_byte: sibling.end_byte(),
3050        start_line: node.start_position().row + 1,
3051        end_line: sibling.end_position().row + 1,
3052    };
3053    Some(RecoveredFunctionLikeExportClassPair {
3054        name,
3055        raw_supertypes,
3056        base_facts,
3057        range,
3058        fragmented_body: recovered_fragmented_export_body(body, range)?,
3059    })
3060}
3061
3062/// Recover a function-like export-macro class that tree-sitter embedded in a
3063/// larger error after an earlier malformed class body. The grammar still
3064/// preserves every part of the class head: the `class` token, export macro
3065/// identifier and argument list, displaced class identifier, access specifier,
3066/// base field, and initializer-list-shaped body. Match only that complete
3067/// structured sequence and keep each recovered class's exact byte envelope.
3068fn recover_embedded_function_like_export_classes(
3069    node: Node<'_>,
3070    source: &str,
3071) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3072    if !node.is_error() {
3073        return Vec::new();
3074    }
3075
3076    let mut nodes = Vec::new();
3077    let mut class_tokens = Vec::new();
3078    let mut stack = vec![node];
3079    while let Some(current) = stack.pop() {
3080        nodes.push(current);
3081        // An error subtree without an anonymous class token cannot contain
3082        // a recoverable class head. Detect that before sorting or resolving
3083        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3084        if !current.is_named() && current.kind() == "class" {
3085            class_tokens.push(current);
3086        }
3087        push_children_reversed(current, &mut stack);
3088    }
3089    if class_tokens.is_empty() {
3090        return Vec::new();
3091    }
3092    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3093    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3094
3095    // The scans below ask the same handful of kind questions once per node of
3096    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3097    let language = node.language();
3098    let class_kind = NodeKindIds::new(&language, "class");
3099    let identifier_kinds = [
3100        NodeKindIds::new(&language, "identifier"),
3101        NodeKindIds::new(&language, "type_identifier"),
3102        NodeKindIds::new(&language, "field_identifier"),
3103    ];
3104    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3105    let colon_kind = NodeKindIds::new(&language, ":");
3106    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3107
3108    let mut recovered = Vec::new();
3109    for class_token in class_tokens {
3110        debug_assert!(class_kind.matches(class_token));
3111        let row = class_token.start_position().row;
3112        // The head after the `class` token reads `MACRO(args) macros... name
3113        // [final] : bases`. The macro is the first identifier, a function-like
3114        // invocation when its argument list is the next thing after it, and the
3115        // name is the last identifier before the head ends at `final` or at the
3116        // base clause `:`. Spelling plays no part (#2557).
3117        let is_identifier = |candidate: &Node<'_>| {
3118            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3119        };
3120        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3121            candidate.start_byte() >= class_token.end_byte()
3122                && candidate.start_position().row == row
3123                && is_identifier(candidate)
3124        }) else {
3125            continue;
3126        };
3127        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3128            argument_list_kind.matches(*candidate)
3129                && candidate.start_byte() >= macro_name.end_byte()
3130                && candidate.start_position().row == row
3131        }) else {
3132            continue;
3133        };
3134        if nodes.iter().any(|candidate| {
3135            is_identifier(candidate)
3136                && candidate.start_byte() >= macro_name.end_byte()
3137                && candidate.end_byte() <= arguments.start_byte()
3138        }) {
3139            continue;
3140        }
3141        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3142            candidate.start_byte() >= arguments.end_byte()
3143                && (recovered_export_head_final(*candidate, source)
3144                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3145        }) else {
3146            continue;
3147        };
3148        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
3149            is_identifier(candidate)
3150                && candidate.start_byte() >= arguments.end_byte()
3151                && candidate.end_byte() <= head_end.start_byte()
3152                && candidate.start_position().row == row
3153        }) else {
3154            continue;
3155        };
3156        let name = normalize_cpp_whitespace(node_text(name_node, source));
3157        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3158            field_initializer_kind.matches(*candidate)
3159                && candidate.start_byte() >= name_node.end_byte()
3160                && candidate
3161                    .child_by_field_name("field")
3162                    .or_else(|| candidate.named_child(0))
3163                    .is_some()
3164                && candidate
3165                    .child_by_field_name("value")
3166                    .or_else(|| {
3167                        let mut cursor = candidate.walk();
3168                        candidate
3169                            .named_children(&mut cursor)
3170                            .find(|child| child.kind() == "initializer_list")
3171                    })
3172                    .is_some_and(|value| value.kind() == "initializer_list")
3173        }) else {
3174            continue;
3175        };
3176        let has_access = nodes.iter().copied().any(|candidate| {
3177            candidate.start_byte() >= name_node.end_byte()
3178                && candidate.end_byte() <= base_initializer.start_byte()
3179                && matches!(
3180                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3181                    "public" | "protected" | "private"
3182                )
3183        });
3184        if !has_access {
3185            continue;
3186        }
3187        let Some(base_node) = base_initializer
3188            .child_by_field_name("field")
3189            .or_else(|| base_initializer.named_child(0))
3190        else {
3191            continue;
3192        };
3193        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3194            continue;
3195        };
3196        let body = base_initializer
3197            .child_by_field_name("value")
3198            .or_else(|| {
3199                let mut cursor = base_initializer.walk();
3200                base_initializer
3201                    .named_children(&mut cursor)
3202                    .find(|child| child.kind() == "initializer_list")
3203            })
3204            .expect("initializer-list value checked above");
3205        let range = Range {
3206            start_byte: class_token.start_byte(),
3207            end_byte: body.end_byte(),
3208            start_line: class_token.start_position().row + 1,
3209            end_line: body.end_position().row + 1,
3210        };
3211        if recovered
3212            .iter()
3213            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3214                existing.name == name && existing.range == range
3215            })
3216        {
3217            continue;
3218        }
3219        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3220            continue;
3221        };
3222        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3223            name,
3224            range,
3225            raw_supertypes: vec![base],
3226            base_facts: vec![base_fact],
3227            fragmented_body: match recovered_fragmented_export_body(body, range) {
3228                Some(fragmented) => fragmented,
3229                None => continue,
3230            },
3231        });
3232    }
3233    recovered
3234}
3235
3236fn lifted_function_like_export_class_namespace<'tree>(
3237    node: Node<'tree>,
3238    source: &str,
3239    ancestry: &ParentIndex<'tree>,
3240) -> Option<String> {
3241    // A long malformed body can embed the next exported class several levels
3242    // below a bogus top-level function_definition. Compare namespace evidence
3243    // against that top-level envelope, not only the recovered ERROR's direct
3244    // parent. The source tree still proves the same boundary: one earlier
3245    // malformed namespace and one later standalone closing brace.
3246    let mut anchor = node;
3247    let parent = loop {
3248        let parent = ancestry.parent(anchor)?;
3249        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3250            break parent;
3251        }
3252        anchor = parent;
3253    };
3254    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3255        sibling.start_byte() > anchor.end_byte()
3256            && sibling.kind() == "ERROR"
3257            && sibling.named_child_count() == 0
3258            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3259    });
3260    if !has_later_close {
3261        return None;
3262    }
3263    let candidates = parent
3264        .named_children(&mut parent.walk())
3265        .filter(|sibling| {
3266            sibling.kind() == "namespace_definition"
3267                && sibling.has_error()
3268                && sibling.end_byte() < anchor.start_byte()
3269        })
3270        .filter_map(|namespace| {
3271            namespace
3272                .child_by_field_name("name")
3273                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3274                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3275        })
3276        .collect::<Vec<_>>();
3277    let [namespace] = candidates.as_slice() else {
3278        return None;
3279    };
3280    Some(namespace.clone())
3281}
3282
3283pub(crate) fn recovered_function_like_export_class_pair_has_body(
3284    node: Node<'_>,
3285    source: &str,
3286    identifier: &str,
3287    range: &Range,
3288) -> bool {
3289    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3290        recovered.name == identifier
3291            && recovered.range.start_byte == range.start_byte
3292            && recovered.range.end_byte == range.end_byte
3293    })
3294}
3295
3296fn recovered_exported_class_has_body(
3297    node: Node<'_>,
3298    source: &str,
3299    expected_name: &str,
3300) -> Option<bool> {
3301    match node.kind() {
3302        "function_definition" => {
3303            let (class_node, name, _, _) =
3304                recover_exported_class_function_definition(node, source)?;
3305            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3306        }
3307        "declaration" | "field_declaration" => {
3308            let recovered = recover_exported_class_declaration(node, source)?;
3309            (recovered.name == expected_name).then(|| recovered.body.is_some())
3310        }
3311        _ => None,
3312    }
3313}
3314
3315/// One file's embedded export-macro class recovery, resolved once and keyed by
3316/// the `ERROR` node that mints each set.
3317///
3318/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3319/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3320/// once per class-like unit in the file. On a translation unit the parser could
3321/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3322/// node spans most of the file -- that is one full subtree pass per class,
3323/// quadratic in the file's size, and it was 78% of that file's inverse scan
3324/// (#1496).
3325///
3326/// Keyed by byte span rather than node identity, so one analyzer generation's
3327/// re-parses of the same content share the index. A nested `ERROR` that shares
3328/// its parent's span recovers the same classes: the only node the parent adds
3329/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3330#[derive(Default)]
3331pub struct CppRecoveredExportClassIndex {
3332    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3333}
3334
3335impl CppRecoveredExportClassIndex {
3336    pub fn build(root: Node<'_>, source: &str) -> Self {
3337        let mut by_error_node: HashMap<
3338            (usize, usize),
3339            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3340        > = HashMap::default();
3341        // One cursor for the whole-file walk, and the error question asked by
3342        // symbol rather than by `kind()` string (#3097).
3343        let mut cursor = root.walk();
3344        let mut stack = vec![root];
3345        while let Some(node) = stack.pop() {
3346            if node.is_error() {
3347                let recovered = recover_embedded_function_like_export_classes(node, source);
3348                if !recovered.is_empty() {
3349                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3350                }
3351            }
3352            stack.extend(node.named_children(&mut cursor));
3353        }
3354        Self { by_error_node }
3355    }
3356
3357    /// The bytes this index holds, for the analyzer cache's weight.
3358    pub fn approximate_size(&self) -> usize {
3359        self.by_error_node
3360            .values()
3361            .fold(0usize, |total, recovered| {
3362                recovered.iter().fold(
3363                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3364                    |acc, class| {
3365                        acc.saturating_add(std::mem::size_of::<
3366                            RecoveredEmbeddedFunctionLikeExportClass,
3367                        >())
3368                        .saturating_add(class.name.len())
3369                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3370                    },
3371                )
3372            })
3373    }
3374
3375    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3376        self.by_error_node
3377            .get(&(node.start_byte(), node.end_byte()))
3378            .is_some_and(|recovered| {
3379                recovered.iter().any(|class| {
3380                    class.name == identifier
3381                        && class.range.start_byte == range.start_byte
3382                        && class.range.end_byte == range.end_byte
3383                })
3384            })
3385    }
3386}
3387
3388// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3389// `recovered_class_body_at` pops while deciding whether a recovered class shape
3390// claims one declaration range. The count is deterministic for a given source,
3391// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3392// pins it directly instead of timing the walk, the way #2358 pinned the
3393// `remove_code_unit` scan.
3394#[cfg(any(test, feature = "test-support"))]
3395thread_local! {
3396    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3397        const { std::cell::Cell::new(0) };
3398}
3399
3400/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3401/// the calling thread since the last
3402/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3403#[cfg(any(test, feature = "test-support"))]
3404#[doc(hidden)]
3405pub fn recovered_class_body_node_visits_for_test() -> usize {
3406    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3407}
3408
3409/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3410#[cfg(any(test, feature = "test-support"))]
3411#[doc(hidden)]
3412pub fn reset_recovered_class_body_node_visits_for_test() {
3413    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3414}
3415
3416#[cfg(any(test, feature = "test-support"))]
3417fn record_recovered_class_body_visit() {
3418    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3419}
3420
3421#[cfg(not(any(test, feature = "test-support")))]
3422fn record_recovered_class_body_visit() {}
3423
3424/// Whether a recovered class shape named `identifier` owns `range`, and if so
3425/// whether that shape has a body.
3426///
3427/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3428/// forward declaration, and `None` means no recovered shape claims the range,
3429/// so the caller reads the plain `class_specifier` family instead. This is the
3430/// single definition of "does a recovered class have a body": the resolver's
3431/// declaration-strength answer and the navigation occurrence role both read it,
3432/// so an export-macro class is a definition on both paths.
3433///
3434/// The walk follows only the nodes whose span covers `range.start_byte`, which
3435/// is every node that can answer. Each recovered shape reports a range that
3436/// starts at the node's own start byte (the function-like export pair, whose
3437/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3438/// class, which the caller gates on an equal start) or at a token inside the
3439/// node (the embedded export class, keyed on its `class` token, and the
3440/// exported class wrapper, gated on containment here), and every one of them is
3441/// accepted only on an exact match with `range`. Descending everywhere instead
3442/// made one declaration-strength question cost a full pass over the file, so
3443/// asking it once per reference was quadratic in file size: 80% of the 385 s
3444/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3445/// (#1496).
3446pub(crate) fn recovered_class_body_at(
3447    recovered_export_classes: &CppRecoveredExportClassIndex,
3448    root: Node<'_>,
3449    source: &str,
3450    identifier: &str,
3451    range: &Range,
3452) -> Option<bool> {
3453    let covers_range_start = |node: &Node<'_>| {
3454        node.start_byte() <= range.start_byte
3455            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3456    };
3457    let mut stack = vec![root];
3458    let mut saw_forward = false;
3459    while let Some(node) = stack.pop() {
3460        record_recovered_class_body_visit();
3461        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3462        // so an unequal start settles it before the recovery reads the node's
3463        // children at all.
3464        if (node.start_byte() == range.start_byte
3465            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3466            || recovered_export_classes.claims(node, identifier, range)
3467            || (node.start_byte() == range.start_byte
3468                && recovered_fragmented_class_has_body(node, source, identifier, range))
3469        {
3470            return Some(true);
3471        }
3472        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3473            return Some(true);
3474        }
3475        // Macro-decorated exported classes are recovered from a malformed
3476        // function_definition/declaration wrapper. Their indexed class range starts at
3477        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3478        // may also extend the indexed range beyond the wrapper through trailing class
3479        // fragments. Match the structured container that owns the range start by its
3480        // recovered name instead of requiring identical boundaries.
3481        if node.start_byte() <= range.start_byte
3482            && range.start_byte < node.end_byte()
3483            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3484        {
3485            if has_body {
3486                return Some(true);
3487            }
3488            saw_forward = true;
3489            continue;
3490        }
3491        let mut cursor = node.walk();
3492        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3493    }
3494    saw_forward.then_some(false)
3495}
3496
3497/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3498/// head of `identifier`'s member list at `range`.
3499///
3500/// An aggregate whose member list opens with a field-list macro invocation
3501/// never forms a specifier, so the ordinary strength read finds no specifier
3502/// node at the indexed range and answers `Unknown`. A complete definition
3503/// recovered this way then loses the defining-type choice to a forward
3504/// declaration of the same tag in another header, and every member the
3505/// definition declares becomes unreachable through it: libuv's
3506/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3507/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3508/// proves the body, so it answers for its own ranges.
3509fn recovered_collapsed_aggregate_has_body(
3510    node: Node<'_>,
3511    source: &str,
3512    identifier: &str,
3513    range: &Range,
3514) -> bool {
3515    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3516        head.key.start_byte() == range.start_byte
3517            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3518    };
3519    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3520        return true;
3521    }
3522    if !node.is_error() {
3523        return false;
3524    }
3525    let mut cursor = node.walk();
3526    let children = node.children(&mut cursor).collect::<Vec<_>>();
3527    children
3528        .iter()
3529        .enumerate()
3530        .filter(|(_, child)| {
3531            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3532        })
3533        .any(|(index, _)| {
3534            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3535        })
3536}
3537
3538/// Whether `node` is the base type displaced into the declarator field of an
3539/// export-macro class that tree-sitter represented as a declaration or
3540/// function definition.
3541///
3542/// Declaration extraction already recovers this exact malformed envelope as a
3543/// class and records the declarator as its base. Reference extraction must use
3544/// the same structural fact instead of treating the node as a function name.
3545pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3546    if !matches!(
3547        node.kind(),
3548        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3549    ) {
3550        return false;
3551    }
3552    if let Some(function) = node.parent().filter(|parent| {
3553        parent.kind() == "function_definition"
3554            && parent
3555                .child_by_field_name("declarator")
3556                .is_some_and(|declarator| same_node(declarator, node))
3557    }) {
3558        return recover_exported_class_function_definition(function, source)
3559            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3560    }
3561    let Some(initializer) = node.parent().filter(|parent| {
3562        parent.kind() == "init_declarator"
3563            && parent
3564                .child_by_field_name("declarator")
3565                .is_some_and(|declarator| same_node(declarator, node))
3566    }) else {
3567        return false;
3568    };
3569    initializer
3570        .parent()
3571        .filter(|parent| parent.kind() == "declaration")
3572        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3573        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3574}
3575
3576/// Recover the class item from a region reparse that still carries the
3577/// sentinel's synthetic function envelope.  An unknown class attribute can
3578/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3579/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3580/// then nested below that function, so direct class-child lookup is not enough.
3581struct CppSentinelReparsedClass<'tree> {
3582    declaration_node: Node<'tree>,
3583    name: String,
3584    body: Node<'tree>,
3585    raw_supertypes: Option<Vec<String>>,
3586    base_facts: Option<Vec<CppBaseSpecifierFact>>,
3587}
3588
3589fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3590    let mut cursor = root.walk();
3591    root.named_children(&mut cursor)
3592        .find(|child| child.kind() != "comment")
3593        .filter(|child| child.kind() == "template_declaration")
3594}
3595
3596fn cpp_sentinel_reparsed_class<'tree>(
3597    root: Node<'tree>,
3598    template_node: Option<Node<'tree>>,
3599    source: &str,
3600    ancestry: &ParentIndex<'tree>,
3601) -> Option<CppSentinelReparsedClass<'tree>> {
3602    let container = template_node.unwrap_or(root);
3603    let mut cursor = container.walk();
3604    for child in container.named_children(&mut cursor) {
3605        if matches!(
3606            child.kind(),
3607            "class_specifier" | "struct_specifier" | "union_specifier"
3608        ) {
3609            let name = class_like_name(child, source, ancestry)?;
3610            let body = cpp_body_node(child)?;
3611            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3612                .then(|| extract_cpp_supertypes(child, source));
3613            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
3614                .then(|| extract_cpp_base_facts(child, source));
3615            return Some(CppSentinelReparsedClass {
3616                declaration_node: child,
3617                name,
3618                body,
3619                raw_supertypes,
3620                base_facts,
3621            });
3622        }
3623        if child.kind() == "declaration"
3624            && let Some(class_node) = first_class_like_child(child)
3625        {
3626            let name = class_like_name(class_node, source, ancestry)?;
3627            let body = cpp_body_node(class_node)?;
3628            let raw_supertypes =
3629                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3630                    .then(|| extract_cpp_supertypes(class_node, source));
3631            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3632                .then(|| extract_cpp_base_facts(class_node, source));
3633            return Some(CppSentinelReparsedClass {
3634                declaration_node: class_node,
3635                name,
3636                body,
3637                raw_supertypes,
3638                base_facts,
3639            });
3640        }
3641        // Only when the nested class item carries its own body. A bodyless
3642        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3643        // a function definition -- is the export-macro shape recovered by the
3644        // next arm, and must fall through to it rather than abort the search.
3645        if child.kind() == "function_definition"
3646            && let Some(class_node) = first_class_like_child(child)
3647            && let Some(body) = cpp_body_node(class_node)
3648            && let Some(name) = class_like_name(class_node, source, ancestry)
3649        {
3650            let raw_supertypes =
3651                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3652                    .then(|| extract_cpp_supertypes(class_node, source));
3653            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3654                .then(|| extract_cpp_base_facts(class_node, source));
3655            return Some(CppSentinelReparsedClass {
3656                declaration_node: class_node,
3657                name,
3658                body,
3659                raw_supertypes,
3660                base_facts,
3661            });
3662        }
3663        if child.kind() == "function_definition"
3664            && let Some((_, name, raw_supertypes, base_facts)) =
3665                recover_exported_class_function_definition(child, source)
3666        {
3667            let body = cpp_body_node(child)?;
3668            return Some(CppSentinelReparsedClass {
3669                declaration_node: child,
3670                name,
3671                body,
3672                raw_supertypes,
3673                base_facts,
3674            });
3675        }
3676    }
3677    None
3678}
3679
3680fn recovered_postfix_export_macro_base(
3681    node: Node<'_>,
3682    type_node: Node<'_>,
3683    declarator: Node<'_>,
3684    source: &str,
3685) -> Option<String> {
3686    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
3687        .and_then(|base| recovered_malformed_base_name(base, source))
3688}
3689
3690fn recovered_postfix_export_macro_base_node<'tree>(
3691    node: Node<'tree>,
3692    type_node: Node<'tree>,
3693    declarator: Node<'tree>,
3694    source: &str,
3695) -> Option<Node<'tree>> {
3696    let mut cursor = node.walk();
3697    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3698        child.kind() == "ERROR"
3699            && child.start_byte() >= type_node.end_byte()
3700            && child.end_byte() <= declarator.start_byte()
3701            && postfix_export_macro_inheritance(*child, source)
3702    });
3703    malformed_clauses.next()?;
3704    if malformed_clauses.next().is_some() {
3705        return None;
3706    }
3707    Some(declarator)
3708}
3709
3710fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3711    let mut macro_count = 0;
3712    let mut colon_count = 0;
3713    let mut access_count = 0;
3714    for index in 0..node.child_count() {
3715        let Some(child) = node.child(index) else {
3716            return false;
3717        };
3718        match child.kind() {
3719            "identifier" | "type_identifier" if child.is_named() => {
3720                let candidate = normalize_cpp_whitespace(node_text(child, source));
3721                if !cpp_export_macro_token(&candidate) {
3722                    return false;
3723                }
3724                macro_count += 1;
3725            }
3726            ":" if !child.is_named() => colon_count += 1,
3727            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3728            _ => return false,
3729        }
3730    }
3731    macro_count == 1 && colon_count == 1 && access_count == 1
3732}
3733
3734fn recovered_single_base_after_declarator_node<'tree>(
3735    node: Node<'tree>,
3736    declarator: Node<'tree>,
3737    source: &str,
3738) -> Option<Node<'tree>> {
3739    let body_start = node
3740        .child_by_field_name("body")
3741        .map(|body| body.start_byte())
3742        .unwrap_or(node.end_byte());
3743    let mut cursor = node.walk();
3744    let mut bases = node
3745        .named_children(&mut cursor)
3746        .filter(|child| {
3747            child.kind() == "ERROR"
3748                && child.start_byte() >= declarator.end_byte()
3749                && child.end_byte() <= body_start
3750        })
3751        .filter_map(|error| {
3752            displaced_exported_class_name(error, source)?;
3753            let mut error_cursor = error.walk();
3754            error
3755                .named_children(&mut error_cursor)
3756                .find(|child| recovered_malformed_base_name(*child, source).is_some())
3757        });
3758    let base = bases.next()?;
3759    bases.next().is_none().then_some(base)
3760}
3761
3762fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3763    (0..node.child_count()).any(|index| {
3764        node.child(index)
3765            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3766    })
3767}
3768
3769pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3770    recover_exported_class_function_definition(node, source).is_some()
3771}
3772
3773fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3774    matches!(
3775        kind,
3776        "ERROR"
3777            | "preproc_if"
3778            | "preproc_ifdef"
3779            | "preproc_ifndef"
3780            | "preproc_else"
3781            | "preproc_elif"
3782    ) || (kind == "labeled_statement" && in_class_scope)
3783}
3784
3785pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3786    if node.kind() != "declaration" {
3787        return false;
3788    }
3789    let mut ancestor = node.parent();
3790    while let Some(container) = ancestor {
3791        match container.kind() {
3792            "compound_statement" => {
3793                return container.parent().is_some_and(|class_container| {
3794                    is_recovered_exported_class_container(class_container, source)
3795                });
3796            }
3797            // These containers preserve ScopeInfo in visit_node. declaration_list is
3798            // the body container selected for a linkage specification.
3799            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3800            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3801            _ => return false,
3802        }
3803        ancestor = container.parent();
3804    }
3805    false
3806}
3807
3808fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3809    let body_start = node
3810        .child_by_field_name("body")
3811        .map(|body| body.start_byte())
3812        .unwrap_or(node.end_byte());
3813    let mut stack = Vec::new();
3814    for index in (0..node.named_child_count()).rev() {
3815        let Some(child) = node.named_child(index) else {
3816            continue;
3817        };
3818        if child.start_byte() >= body_start {
3819            continue;
3820        }
3821        stack.push(child);
3822    }
3823
3824    let mut best = None;
3825    while let Some(current) = stack.pop() {
3826        if matches!(current.kind(), "identifier" | "type_identifier") {
3827            let name = normalize_cpp_whitespace(node_text(current, source));
3828            if !name.is_empty()
3829                && !cpp_export_macro_token(&name)
3830                && !matches!(name.as_str(), "class" | "struct" | "union")
3831            {
3832                best = Some(name);
3833            }
3834            continue;
3835        }
3836
3837        for index in (0..current.named_child_count()).rev() {
3838            if let Some(child) = current.named_child(index)
3839                && child.start_byte() < body_start
3840            {
3841                stack.push(child);
3842            }
3843        }
3844    }
3845    best
3846}
3847
3848fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3849    if node.kind() == "declaration"
3850        && node
3851            .child_by_field_name("type")
3852            .or_else(|| first_class_like_child(node))
3853            .is_some_and(|type_node| {
3854                matches!(
3855                    type_node.kind(),
3856                    "class_specifier" | "struct_specifier" | "union_specifier"
3857                )
3858            })
3859        && let Some(name) = node
3860            .child_by_field_name("declarator")
3861            .and_then(|declarator| declarator_name_from_node(declarator, source))
3862        && !cpp_export_macro_token(&name)
3863    {
3864        return Some(name);
3865    }
3866
3867    if node.kind() == "function_definition"
3868        && node.child_by_field_name("type").is_some_and(|type_node| {
3869            matches!(
3870                type_node.kind(),
3871                "class_specifier" | "struct_specifier" | "union_specifier"
3872            )
3873        })
3874        && let Some(name) = node
3875            .child_by_field_name("declarator")
3876            .and_then(|declarator| direct_identifier_name(declarator, source))
3877        && !cpp_export_macro_token(&name)
3878    {
3879        return Some(name);
3880    }
3881
3882    let class_node = if matches!(
3883        node.kind(),
3884        "class_specifier" | "struct_specifier" | "union_specifier"
3885    ) {
3886        node
3887    } else {
3888        first_class_like_child(node)?
3889    };
3890    class_like_name_from_children(class_node, source)
3891}
3892
3893fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3894    if !matches!(
3895        node.kind(),
3896        "identifier" | "field_identifier" | "type_identifier"
3897    ) {
3898        return None;
3899    }
3900    let name = normalize_cpp_whitespace(node_text(node, source));
3901    (!name.is_empty()).then_some(name)
3902}
3903
3904fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3905    match node.kind() {
3906        "identifier" | "field_identifier" | "type_identifier" => {
3907            let name = normalize_cpp_whitespace(node_text(node, source));
3908            (!name.is_empty()).then_some(name)
3909        }
3910        _ => {
3911            let mut cursor = node.walk();
3912            node.named_children(&mut cursor)
3913                .find_map(|child| declarator_name_from_node(child, source))
3914        }
3915    }
3916}
3917
3918fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3919    let mut cursor = node.walk();
3920    node.named_children(&mut cursor).find(|child| {
3921        matches!(
3922            child.kind(),
3923            "class_specifier" | "struct_specifier" | "union_specifier"
3924        )
3925    })
3926}
3927
3928/// Push a container's children as a `Siblings` cursor rather than snapshotting
3929/// them all with one shared scope: children are visited one at a time so a
3930/// `using namespace X;` sibling can affect the scope threaded to the siblings
3931/// that textually follow it (issue #1093).
3932fn push_cpp_container_work<'tree>(
3933    node: Node<'tree>,
3934    scope: ScopeInfo,
3935    stack: &mut Vec<CppWork<'tree>>,
3936) {
3937    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3938}
3939
3940/// Materialize one selected named-child range with a tree-sitter cursor. The
3941/// cursor advances linearly across the parent's concrete children; repeatedly
3942/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3943fn push_cpp_sibling_range<'tree>(
3944    parent: Node<'tree>,
3945    start_index: usize,
3946    end_index: usize,
3947    scope: ScopeInfo,
3948    stack: &mut Vec<CppWork<'tree>>,
3949) {
3950    let mut cursor = parent.walk();
3951    let children = parent
3952        .named_children(&mut cursor)
3953        .skip(start_index)
3954        .take(end_index.saturating_sub(start_index))
3955        .collect::<Vec<_>>()
3956        .into_iter();
3957    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3958}
3959
3960/// Advance a `Siblings` cursor by one child: dispatch the current child under
3961/// the scope accumulated from its *earlier* siblings, then push a
3962/// continuation for the remaining siblings carrying the scope updated for
3963/// *this* child (only `using namespace X;` directives change it). Pushing the
3964/// continuation before the current child's own node work means the current
3965/// child's subtree fully drains (LIFO) before the next sibling is visited,
3966/// preserving left-to-right order.
3967fn advance_cpp_siblings<'tree>(
3968    mut siblings: CppSiblingsWork<'tree>,
3969    source: &str,
3970    stack: &mut Vec<CppWork<'tree>>,
3971) {
3972    let Some(child) = siblings.children.next() else {
3973        return;
3974    };
3975    let current_scope = siblings.scope.clone();
3976    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3977        siblings.scope.visible_using_namespaces.push(namespace);
3978    }
3979    if !siblings.children.as_slice().is_empty() {
3980        stack.push(CppWork::Siblings(siblings));
3981    }
3982    stack.push(CppWork::Node(CppNodeWork {
3983        node: child,
3984        scope: current_scope,
3985    }));
3986}
3987
3988/// The namespace target of a `using namespace X;` directive, or `None` for
3989/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3990/// kind. Distinguished structurally by the presence of the grammar's literal
3991/// `namespace` keyword token among the node's children -- not by inspecting
3992/// source text -- so it never misreads a member-importing using-declaration
3993/// as a namespace directive.
3994fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3995    if node.kind() != "using_declaration" {
3996        return None;
3997    }
3998    let mut cursor = node.walk();
3999    let is_namespace_directive = node
4000        .children(&mut cursor)
4001        .any(|child| child.kind() == "namespace");
4002    if !is_namespace_directive {
4003        return None;
4004    }
4005    let target = node.named_child(0)?;
4006    // A leading `::` is the explicit-global marker, not part of the namespace
4007    // path (`using namespace ::std::chrono;`). Drop that AST token before
4008    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4009    // keeps: storing the marker verbatim desynced the legacy package string from
4010    // the FqName bridge, which splits on `::` and drops the empty leading
4011    // component, tripping the package/short boundary assert when a bare-owner
4012    // out-of-line definition borrowed the directive's namespace (#1093 path).
4013    let start = target
4014        .child(0)
4015        .filter(|child| !child.is_named() && child.kind() == "::")
4016        .map_or(target.start_byte(), |marker| marker.end_byte());
4017    let text = normalize_cpp_whitespace(
4018        source
4019            .get(start..target.end_byte())
4020            .expect("using-directive target covers one source range"),
4021    );
4022    (!text.is_empty()).then_some(text)
4023}
4024
4025/// Extract all requested dialect readings from one primary AST event stream.
4026/// Declaration schedulers retain their own admission, recovery and sibling order;
4027/// structural facts, includes and identifiers are interpreted once for the tree.
4028pub(crate) fn parse_cpp_readings<'tree>(
4029    file: &ProjectFile,
4030    source: &str,
4031    root: Node<'tree>,
4032    ancestry: &ParentIndex<'tree>,
4033    dialects: &[LanguageDialect],
4034) -> Vec<ParsedFile> {
4035    parse_cpp_readings_with_object_macro_fields(
4036        file,
4037        source,
4038        root,
4039        ancestry,
4040        dialects,
4041        HashMap::default(),
4042    )
4043}
4044
4045pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4046    file: &ProjectFile,
4047    source: &str,
4048    root: Node<'tree>,
4049    ancestry: &ParentIndex<'tree>,
4050    dialects: &[LanguageDialect],
4051    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4052) -> Vec<ParsedFile> {
4053    assert!(
4054        dialects.len() == 1
4055            || (dialects.len() == 2
4056                && dialects[0] != LanguageDialect::CppC
4057                && dialects[1] == LanguageDialect::CppC),
4058        "one primary dialect, optionally followed by its C alternative"
4059    );
4060    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4061    let properties = RefCell::new(HashMap::default());
4062    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4063    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4064    let mut using_namespaces = Vec::new();
4065    let mut readings: Vec<_> = dialects
4066        .iter()
4067        .map(|_| ParsedFile::new(String::new()))
4068        .collect();
4069    let mut visitors: Vec<_> = readings
4070        .iter_mut()
4071        .zip(dialects)
4072        .map(|(parsed, dialect)| CppVisitor {
4073            file,
4074            source,
4075            parsed,
4076            source_collector: &collector,
4077            source_properties: &properties,
4078            source_dependencies: &dependencies,
4079            primary_ancestry: ancestry,
4080            primary_root_id: root.id(),
4081            source_links: HashMap::default(),
4082            source_declarations: HashSet::default(),
4083            c_tag_semantics: *dialect == LanguageDialect::CppC,
4084            c_tag_scope_witness: false,
4085            recovered_class_sibling_scopes: HashMap::default(),
4086            consumed_fragment_regions: Vec::new(),
4087            orphaned_namespaces: &orphaned_namespaces,
4088            partitioned_regions: Vec::new(),
4089            namespace_forward_scans: HashMap::default(),
4090            field_owners: None,
4091            recovery_captures: Vec::new(),
4092            active_source_context: None,
4093            object_macro_fields: object_macro_fields.clone(),
4094            ambiguous_object_macro_fields: HashSet::default(),
4095        })
4096        .collect();
4097    let mut declaration_stacks: Vec<_> = dialects
4098        .iter()
4099        .map(|_| {
4100            vec![CppWork::Container(CppContainer {
4101                node: root,
4102                scope: ScopeInfo {
4103                    package_name: String::new(),
4104                    module: None,
4105                    class_unit: None,
4106                    template_signature: None,
4107                    template_metadata: None,
4108                    declarations_are_fields: false,
4109                    recovered_specialization_member_scope: false,
4110                    visible_using_namespaces: Vec::new(),
4111                },
4112            })]
4113        })
4114        .collect();
4115    let kinds = CompiledKinds::compile(
4116        &tree_sitter_cpp::LANGUAGE.into(),
4117        CPP_STRUCTURAL_SPEC.kind_table(),
4118    );
4119    // Forward macro names affect calls before their definitions and require the
4120    // existing bounded preprocessor-only context prepass.
4121    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4122    let mut structural = StructuralFactCollector::new(
4123        &CPP_STRUCTURAL_SPEC,
4124        source,
4125        &context,
4126        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4127        usize::MAX,
4128        None,
4129    );
4130    let mut imports = Vec::new();
4131    let mut includes = Vec::new();
4132    let mut identifiers = HashSet::default();
4133    let mut entered = HashSet::default();
4134    let mut primary_ordinals = HashMap::default();
4135    let mut stack = vec![(root, None)];
4136    while let Some((node, parent)) = stack.pop() {
4137        assert!(entered.insert(node.id()), "one primary event per AST node");
4138        primary_ordinals.insert(node.id(), primary_ordinals.len());
4139        dependencies.borrow_mut().primary.enter(node, source);
4140        let mut child_parent = parent;
4141        if node.is_named() {
4142            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4143                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4144            }
4145            if let Some(raw_kind) = kinds.kind_of(&node)
4146                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4147            {
4148                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4149                    node,
4150                    raw_kind,
4151                    parent.map(|id| structural.normalized_kind(id)),
4152                    source,
4153                    &context,
4154                );
4155                let mut source_facts = collector.borrow_mut();
4156                let id = structural
4157                    .enter(node, kind, parent, &mut source_facts)
4158                    .expect("unbounded C++ structural collector");
4159                let mut sink = structural.role_sink(&mut source_facts);
4160                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4161                structural
4162                    .accept_roles(id, sink.into_parts())
4163                    .expect("unbounded C++ structural roles");
4164                child_parent = Some(id);
4165            }
4166            if node.kind() == "preproc_include" {
4167                let raw = normalize_cpp_whitespace(node_text(node, source));
4168                if !raw.is_empty() {
4169                    let occurrence = collector.borrow_mut().intern_node(node);
4170                    let target = node.child_by_field_name("path").and_then(|path| {
4171                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4172                        let target = collector.borrow_mut().intern_node(path);
4173                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4174                            declaration: occurrence,
4175                            target,
4176                            path: value.to_owned(),
4177                            quoted,
4178                        });
4179                        Some(target)
4180                    });
4181                    imports.push(SourceImportFact::from_import(
4182                        cpp_include_info(raw),
4183                        occurrence,
4184                        target,
4185                        None,
4186                        Vec::new(),
4187                    ));
4188                }
4189            }
4190            if matches!(
4191                node.kind(),
4192                "type_identifier" | "identifier" | "qualified_identifier"
4193            ) {
4194                let text = node_text(node, source).trim();
4195                if !text.is_empty() {
4196                    identifiers.insert(text.to_owned());
4197                }
4198            }
4199        }
4200        for index in 0..visitors.len() {
4201            // Delay the alternative policy until the primary walk encounters a
4202            // tag whose scope can differ. Its saved declaration stack catches up
4203            // through already-entered events, including recovery, without a
4204            // second AST traversal. A false witness performs no C declaration work.
4205            if index != 0 && !visitors[0].c_tag_scope_witness {
4206                continue;
4207            }
4208            let visitor = &mut visitors[index];
4209            advance_primary_declarations(
4210                visitor,
4211                &mut declaration_stacks[index],
4212                &entered,
4213                ancestry,
4214            );
4215            visitor.drain_partitioned_regions();
4216        }
4217        let mut cursor = node.walk();
4218        let children: Vec<_> = node
4219            .children(&mut cursor)
4220            .map(|child| (child, child_parent))
4221            .collect();
4222        stack.extend(children.into_iter().rev());
4223    }
4224    let reading_count = if visitors[0].c_tag_scope_witness {
4225        visitors.len()
4226    } else {
4227        1
4228    };
4229    visitors.truncate(reading_count);
4230    declaration_stacks.truncate(reading_count);
4231    assert!(
4232        declaration_stacks.iter().all(Vec::is_empty),
4233        "all primary declaration events were consumed"
4234    );
4235    for visitor in &mut visitors {
4236        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4237            .into_iter()
4238            .flat_map(|(unit, declarations)| {
4239                declarations
4240                    .into_iter()
4241                    .map(move |declaration| (declaration, unit.clone()))
4242            })
4243            .collect();
4244    }
4245    let dialect_declarations: Vec<_> = visitors
4246        .iter()
4247        .map(|visitor| visitor.source_declarations.clone())
4248        .collect();
4249    drop(visitors);
4250    readings.truncate(reading_count);
4251    dependencies
4252        .borrow_mut()
4253        .finish_primary(&mut properties.borrow_mut());
4254    // Preserve the existing malformed quoted-directive recovery, but publish its
4255    // explicit source occurrence into the same import family as parsed directives.
4256    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4257    let primary_occurrence_ordinals: HashMap<_, _> = collector
4258        .borrow()
4259        .primary_node_occurrences()
4260        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4261        .collect();
4262    let occurrences = collector.into_inner().finish();
4263    let generic_imports: Vec<_> = (0..imports.len())
4264        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4265        .collect();
4266    let source_facts = ParsedSourceFacts {
4267        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4268            includes,
4269            declarations: {
4270                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4271                facts.sort_by_key(|fact| fact.declaration.get());
4272                facts
4273            },
4274            using_namespaces,
4275        }),
4276        go: None,
4277        java: None,
4278        js_ts: None,
4279        php: None,
4280        scala: None,
4281        ruby: None,
4282        python: None,
4283        source_bytes: source.len(),
4284        occurrences,
4285        structural: structural
4286            .finish()
4287            .expect("C++ structural collection finishes"),
4288        native_site_occurrences: Vec::new(),
4289        native_declaration_sources: Vec::new(),
4290        declaration_visibilities: None,
4291        rust_declaration_properties: Vec::new(),
4292        rust_modules: None,
4293        rust_types: Vec::new(),
4294        rust_items: Default::default(),
4295        imports,
4296        generic_imports,
4297        rust_import_contexts: Vec::new(),
4298    };
4299    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4300        parsed.finalize_deferred_replacements();
4301        let links = std::mem::take(&mut parsed.source_declaration_units);
4302        parsed.source_declaration_units = links
4303            .into_iter()
4304            .filter(|(_, unit)| parsed.contains_declaration(unit))
4305            .collect();
4306        parsed.imports = source_facts
4307            .imports
4308            .iter()
4309            .map(|import| import.import_info(&source_facts.occurrences))
4310            .collect();
4311        parsed.type_identifiers = identifiers.clone();
4312        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4313            &source_facts,
4314            &declarations,
4315            &primary_occurrence_ordinals,
4316            &mut parsed.source_declaration_units,
4317            &mut parsed.source_declaration_metadata,
4318        ));
4319    }
4320    readings
4321}
4322
4323fn advance_primary_declarations<'tree>(
4324    visitor: &mut CppVisitor<'_, '_>,
4325    stack: &mut Vec<CppWork<'tree>>,
4326    entered: &HashSet<usize>,
4327    ancestry: &ParentIndex<'tree>,
4328) {
4329    while let Some(work) = stack.pop() {
4330        match work {
4331            CppWork::Container(container) => {
4332                push_cpp_container_work(container.node, container.scope, stack)
4333            }
4334            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4335            CppWork::Node(work) => {
4336                if !entered.contains(&work.node.id()) {
4337                    stack.push(CppWork::Node(work));
4338                    return;
4339                }
4340                if !visitor.node_is_inside_consumed_fragment(work.node) {
4341                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4342                }
4343            }
4344        }
4345    }
4346}
4347
4348#[derive(Clone)]
4349struct CppActiveSourceContext {
4350    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4351    namespace: Option<String>,
4352    child_namespace: Option<String>,
4353    outer_lexical_scope: Vec<String>,
4354    child_lexical_scope: Vec<String>,
4355    family: Option<SourceOccurrenceId>,
4356    exhaustive_family: Option<SourceOccurrenceId>,
4357    callable_guard_completion_byte: Option<usize>,
4358}
4359
4360/// One owned container reparse deferred until the primary declaration walk has
4361/// reached its source position. The tree and context are owned so the deferred
4362/// walk never retains a `Node` borrowed from a temporary recovery tree.
4363pub struct CppPartitionedRegion {
4364    tree: Tree,
4365    range: std::ops::Range<usize>,
4366    scope: ScopeInfo,
4367    recovery: Range,
4368    source_context: Option<CppActiveSourceContext>,
4369}
4370
4371pub struct CppVisitor<'a, 'source> {
4372    pub file: &'a ProjectFile,
4373    pub source: &'source str,
4374    pub parsed: &'a mut ParsedFile,
4375    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4376    source_properties: &'a RefCell<
4377        HashMap<
4378            SourceDeclarationId,
4379            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4380        >,
4381    >,
4382    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4383    pub primary_ancestry: &'a ParentIndex<'a>,
4384    pub primary_root_id: usize,
4385    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4386    source_declarations: HashSet<SourceDeclarationId>,
4387    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
4388    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
4389    ///
4390    /// C has no nested tag scope: a struct/union/enum tag declared inside
4391    /// another aggregate's member list has the scope of the outer declaration
4392    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
4393    /// therefore declares a file-scope `inner` that a later file-scope
4394    /// `struct inner *p;` legitimately references, where C++ would make the
4395    /// same shape a nested class `outer::inner`. Headers carry no compilation
4396    /// language of their own and keep the conservative C++ interpretation.
4397    pub c_tag_semantics: bool,
4398    /// Sticky evidence that this C++ walk reached a declaration whose scope
4399    /// would change under C tag semantics, including visitor recovery work.
4400    pub(crate) c_tag_scope_witness: bool,
4401    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4402    /// Byte regions whose contents were re-owned by a fragmented export-class
4403    /// recovery (#938): the scattered members between the fragmented
4404    /// declaration and its displaced closing brace are indexed as members of
4405    /// the recovered class by the region reparse, so the ordinary sibling walk
4406    /// must not ALSO index them as top-level declarations (that double-indexing
4407    /// made a scattered nested class ambiguous between `Inner` and
4408    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
4409    /// linear scan at visit time is fine.
4410    pub consumed_fragment_regions: Vec<(usize, usize)>,
4411    /// Lexical namespace corrections and brace boundaries for declarations
4412    /// displaced by parse recovery (issues #1537 and #3087). The parsed
4413    /// ancestors can stop short or extend past their true closes; see
4414    /// [`CppVisitor::recovered_namespace_scope`].
4415    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4416    /// Owned reparses waiting for the outer work loop. Partitioning another
4417    /// swallowed class in a tail must not grow the Rust call stack.
4418    pub partitioned_regions: Vec<CppPartitionedRegion>,
4419    /// The namespace forward declarations already folded out of each tree this
4420    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
4421    /// Empty until the first question, which the overwhelming majority of files
4422    /// never ask.
4423    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
4424    /// Which owners already have field declarations in the parse product, as
4425    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
4426    /// the first enum asks, which most files never do (#2786).
4427    pub field_owners: Option<CppFieldOwnerIndex>,
4428    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
4429    /// happen to the declaration set, innermost last. Empty outside a recovery
4430    /// reparse, which is almost always (#2787).
4431    pub recovery_captures: Vec<CppRecoveryCapture>,
4432    active_source_context: Option<CppActiveSourceContext>,
4433    /// Object-like field-list macros defined earlier in this source. Their
4434    /// replacements are parsed structurally and materialized under each
4435    /// invoking aggregate; no source-text expansion is used.
4436    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4437    /// Names whose active replacement is not a unique structured field list.
4438    /// A later `#undef` resets the ambiguity; another `#define` does not.
4439    pub ambiguous_object_macro_fields: HashSet<String>,
4440}
4441
4442/// One source-order event from an object-like field-list macro environment.
4443///
4444/// The event stream lets an include-closure consumer preserve preprocessor
4445/// invalidation without guessing which parsed header happened to be visited
4446/// first. A conservative consumer may permanently block a name after an
4447/// `undef` when conditional include order is not provable.
4448#[derive(Clone, Debug, PartialEq, Eq)]
4449pub enum ObjectMacroFieldEvent {
4450    Define {
4451        name: String,
4452        replacement: ObjectMacroReplacement,
4453        conditional: bool,
4454    },
4455    Undef {
4456        name: String,
4457        conditional: bool,
4458    },
4459}
4460
4461/// Collect object-like field-list macros from a parsed source in source order.
4462/// Macros with conflicting active replacements are omitted so an include
4463/// closure cannot silently choose one guarded definition over another.
4464pub fn collect_cpp_object_macro_fields<'tree>(
4465    root: Node<'tree>,
4466    source: &str,
4467) -> HashMap<String, ObjectMacroReplacement> {
4468    let mut fields = HashMap::default();
4469    let mut ambiguous = HashSet::default();
4470    for event in collect_cpp_object_macro_field_events(root, source) {
4471        match event {
4472            ObjectMacroFieldEvent::Define {
4473                name,
4474                replacement: value,
4475                conditional,
4476            } => {
4477                if value.is_empty() {
4478                    fields.remove(&name);
4479                    if conditional {
4480                        ambiguous.insert(name);
4481                    } else {
4482                        ambiguous.remove(&name);
4483                    }
4484                } else if ambiguous.contains(&name) {
4485                    // Keep the name blocked until an explicit undef resets it.
4486                } else if let Some(previous) = fields.get(&name) {
4487                    if previous != &value {
4488                        fields.remove(&name);
4489                        ambiguous.insert(name);
4490                    }
4491                } else {
4492                    fields.insert(name, value);
4493                }
4494            }
4495            ObjectMacroFieldEvent::Undef { name, conditional } => {
4496                fields.remove(&name);
4497                if conditional {
4498                    ambiguous.insert(name);
4499                } else {
4500                    ambiguous.remove(&name);
4501                }
4502            }
4503        }
4504    }
4505    fields
4506}
4507
4508/// Collect object-like field-list macro events in source order. Conditional
4509/// branches remain visible in the event stream: callers that cannot prove
4510/// branch selection can invalidate names conservatively instead of inventing
4511/// a replacement from one branch.
4512pub fn collect_cpp_object_macro_field_events<'tree>(
4513    root: Node<'tree>,
4514    source: &str,
4515) -> Vec<ObjectMacroFieldEvent> {
4516    let mut events = Vec::new();
4517    let mut stack = vec![root];
4518    while let Some(node) = stack.pop() {
4519        if node.kind() == "preproc_def"
4520            && let Some(name) = extract_macro_name(node, source)
4521        {
4522            let replacement = object_macro_replacement_of(node, source);
4523            events.push(ObjectMacroFieldEvent::Define {
4524                name,
4525                replacement,
4526                conditional: inside_preprocessor_conditional(node),
4527            });
4528        } else if is_cpp_undef_directive(node, source)
4529            && let Some(argument) = node.child_by_field_name("argument")
4530        {
4531            events.push(ObjectMacroFieldEvent::Undef {
4532                name: node_text(argument, source).trim().to_string(),
4533                conditional: inside_preprocessor_conditional(node),
4534            });
4535        }
4536        let mut cursor = node.walk();
4537        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
4538        stack.extend(children.into_iter().rev());
4539    }
4540    events
4541}
4542
4543fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
4544    let mut current = node.parent();
4545    while let Some(parent) = current {
4546        if matches!(
4547            parent.kind(),
4548            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
4549        ) {
4550            return true;
4551        }
4552        current = parent.parent();
4553    }
4554    false
4555}
4556
4557fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
4558    node.kind() == "preproc_call"
4559        && node
4560            .child_by_field_name("directive")
4561            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
4562}
4563
4564impl<'a, 'source> CppVisitor<'a, 'source> {
4565    fn apply_active_source_context(
4566        &self,
4567        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4568    ) {
4569        let Some(context) = self.active_source_context.as_ref() else {
4570            return;
4571        };
4572        fact.guard_requirements = fact
4573            .guard_requirements
4574            .as_ref()
4575            .and_then(crate::source_context::cpp_guard_set_to_runtime)
4576            .zip(context.guards.as_ref())
4577            .and_then(|(local, outer)| {
4578                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4579            })
4580            .as_ref()
4581            .map(crate::source_context::cpp_guard_set_from_runtime);
4582        if fact.callable_guards.is_some() {
4583            fact.callable_guards = fact
4584                .callable_guards
4585                .as_ref()
4586                .and_then(crate::source_context::cpp_guard_set_to_runtime)
4587                .zip(context.guards.as_ref())
4588                .and_then(|(local, outer)| {
4589                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4590                })
4591                .as_ref()
4592                .map(crate::source_context::cpp_guard_set_from_runtime);
4593        }
4594        fact.callable_guard_completion_byte = fact
4595            .callable_guard_completion_byte
4596            .zip(context.callable_guard_completion_byte)
4597            .map(|(local, outer)| local.max(outer));
4598        fact.conditional_family = fact.conditional_family.or(context.family);
4599        fact.exhaustive_conditional_family = fact
4600            .exhaustive_conditional_family
4601            .or(context.exhaustive_family);
4602        if let Some(alias) = fact.file_scope_alias.as_mut() {
4603            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
4604                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
4605                (Some(outer), None) => Some(outer.clone()),
4606                (None, local) => local,
4607            };
4608        }
4609        if !context.outer_lexical_scope.is_empty() {
4610            let mut lexical = context.outer_lexical_scope.clone();
4611            lexical.append(&mut fact.lexical_path);
4612            fact.lexical_path = lexical;
4613        }
4614    }
4615
4616    fn recovery_ancestry<'tree>(
4617        &self,
4618        root: Node<'tree>,
4619    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
4620        crate::source_dependencies::RecoveryAncestry::new(
4621            root,
4622            self.source,
4623            self.source_dependencies,
4624        )
4625    }
4626
4627    fn capture_source_dependencies(
4628        &self,
4629        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4630        node: Node<'_>,
4631        unit: &CodeUnit,
4632    ) {
4633        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
4634            self.source_dependencies.borrow_mut().register_primary(
4635                fact.declaration,
4636                node,
4637                unit.is_class(),
4638            );
4639        } else {
4640            self.source_dependencies
4641                .borrow()
4642                .apply_recovered(node, unit.is_class(), fact);
4643        }
4644    }
4645
4646    fn link_source_declaration(
4647        &mut self,
4648        ancestry: &ParentIndex<'_>,
4649        unit: &CodeUnit,
4650        node: Node<'_>,
4651    ) {
4652        let (declaration, family) = {
4653            let mut collector = self.source_collector.borrow_mut();
4654            let occurrence = if node.id() == self.primary_root_id
4655                || self.primary_ancestry.contains(node)
4656            {
4657                collector.intern_node(node)
4658            } else {
4659                // A region reparse owns distinct provenance even when its byte range
4660                // equals a primary node or a previously dropped recovery tree.
4661                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
4662            };
4663            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
4664                if self.primary_ancestry.contains(name) {
4665                    collector.intern_node(name)
4666                } else {
4667                    collector
4668                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
4669                }
4670            });
4671            let declaration = collector.declare(occurrence, name);
4672            let family =
4673                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4674                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4675                    {
4676                        collector.intern_node(family)
4677                    } else {
4678                        collector.intern_embedded(
4679                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4680                        )
4681                    }
4682                });
4683            (declaration, family)
4684        };
4685        self.source_declarations.insert(declaration);
4686        if !self.source_properties.borrow().contains_key(&declaration) {
4687            let context =
4688                crate::source_context::capture_declaration_context(node, unit, self.source);
4689            let (exhaustive_family, displaced_namespace_closing_brace) = {
4690                let mut collector = self.source_collector.borrow_mut();
4691                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4692                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4693                    {
4694                        collector.intern_node(family)
4695                    } else {
4696                        collector.intern_embedded(
4697                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4698                        )
4699                    }
4700                });
4701                let displaced_namespace_closing_brace =
4702                    context.displaced_namespace_closing_brace.map(|brace| {
4703                        if brace.id() == self.primary_root_id
4704                            || self.primary_ancestry.contains(brace)
4705                        {
4706                            collector.intern_node(brace)
4707                        } else {
4708                            collector.intern_embedded(
4709                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4710                            )
4711                        }
4712                    });
4713                (exhaustive_family, displaced_namespace_closing_brace)
4714            };
4715            let guard_requirements = context.guard_requirements;
4716            let callable_guards = context.callable_guards;
4717            let callable_activation = context.callable_activation;
4718            let callable_guard_completion_byte = context.callable_guard_completion_byte;
4719            let flattened_macro_namespace = context.flattened_macro_namespace;
4720            self.source_properties
4721                .borrow_mut()
4722                .entry(declaration)
4723                .or_insert_with(|| {
4724                    let mut fact = crate::source_facts::capture_declaration(
4725                        declaration,
4726                        node,
4727                        unit,
4728                        self.source,
4729                        ancestry,
4730                    );
4731                    self.capture_source_dependencies(&mut fact, node, unit);
4732                    fact.occurrence_role =
4733                        crate::identity::cpp_occurrence_role_for_node(node, unit);
4734                    fact.conditional_family = family;
4735                    fact.guard_requirements = guard_requirements;
4736                    fact.callable_guards = callable_guards;
4737                    fact.callable_activation = callable_activation;
4738                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
4739                    fact.exhaustive_conditional_family = exhaustive_family;
4740                    fact.flattened_macro_namespace = flattened_macro_namespace;
4741                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4742                    self.apply_active_source_context(&mut fact);
4743                    fact
4744                });
4745        }
4746        let links = self.source_links.entry(unit.clone()).or_default();
4747        if !links.contains(&declaration) {
4748            links.push(declaration);
4749        }
4750    }
4751
4752    fn link_source_range(
4753        &mut self,
4754        ancestry: &ParentIndex<'_>,
4755        unit: &CodeUnit,
4756        range: Range,
4757        evidence: Option<Node<'_>>,
4758    ) {
4759        let (declaration, family) = {
4760            let mut collector = self.source_collector.borrow_mut();
4761            let occurrence =
4762                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
4763            let name = evidence
4764                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
4765                .map(|name| {
4766                    if self.primary_ancestry.contains(name) {
4767                        collector.intern_node(name)
4768                    } else {
4769                        collector.intern_embedded(
4770                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
4771                        )
4772                    }
4773                });
4774            let declaration = collector.declare(occurrence, name);
4775            let family = evidence.and_then(|node| {
4776                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4777                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4778                    {
4779                        collector.intern_node(family)
4780                    } else {
4781                        collector.intern_embedded(
4782                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4783                        )
4784                    }
4785                })
4786            });
4787            (declaration, family)
4788        };
4789        self.source_declarations.insert(declaration);
4790        if let Some(node) = evidence
4791            && !self.source_properties.borrow().contains_key(&declaration)
4792        {
4793            let context =
4794                crate::source_context::capture_declaration_context(node, unit, self.source);
4795            let (exhaustive_family, displaced_namespace_closing_brace) = {
4796                let mut collector = self.source_collector.borrow_mut();
4797                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4798                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4799                    {
4800                        collector.intern_node(family)
4801                    } else {
4802                        collector.intern_embedded(
4803                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4804                        )
4805                    }
4806                });
4807                let displaced_namespace_closing_brace =
4808                    context.displaced_namespace_closing_brace.map(|brace| {
4809                        if brace.id() == self.primary_root_id
4810                            || self.primary_ancestry.contains(brace)
4811                        {
4812                            collector.intern_node(brace)
4813                        } else {
4814                            collector.intern_embedded(
4815                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4816                            )
4817                        }
4818                    });
4819                (exhaustive_family, displaced_namespace_closing_brace)
4820            };
4821            let guard_requirements = context.guard_requirements;
4822            let callable_guards = context.callable_guards;
4823            let callable_activation = context.callable_activation;
4824            let callable_guard_completion_byte = context.callable_guard_completion_byte;
4825            let flattened_macro_namespace = context.flattened_macro_namespace;
4826            let mut fact = crate::source_facts::capture_declaration(
4827                declaration,
4828                node,
4829                unit,
4830                self.source,
4831                ancestry,
4832            );
4833            self.capture_source_dependencies(&mut fact, node, unit);
4834            fact.occurrence_role = crate::identity::cpp_occurrence_role_for_node(node, unit);
4835            fact.conditional_family = family;
4836            fact.guard_requirements = guard_requirements;
4837            fact.callable_guards = callable_guards;
4838            fact.callable_activation = callable_activation;
4839            fact.callable_guard_completion_byte = callable_guard_completion_byte;
4840            fact.exhaustive_conditional_family = exhaustive_family;
4841            fact.flattened_macro_namespace = flattened_macro_namespace;
4842            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4843            self.apply_active_source_context(&mut fact);
4844            self.source_properties
4845                .borrow_mut()
4846                .insert(declaration, fact);
4847        }
4848        let links = self.source_links.entry(unit.clone()).or_default();
4849        if !links.contains(&declaration) {
4850            links.push(declaration);
4851        }
4852    }
4853
4854    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
4855        let declaration = *self
4856            .source_links
4857            .get(&unit)
4858            .and_then(|links| links.last())
4859            .expect("signature metadata follows its source declaration");
4860        let metadata_ordinal = self
4861            .parsed
4862            .add_signature_with_metadata(unit.clone(), metadata);
4863        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
4864            declaration,
4865            unit,
4866            metadata_ordinal,
4867        };
4868        if !self.parsed.source_declaration_metadata.contains(&link) {
4869            self.parsed.source_declaration_metadata.push(link);
4870        }
4871    }
4872
4873    /// Records `code_unit` with the answers the walk carries forward, then adds
4874    /// it to the parse product.
4875    ///
4876    /// Every declaration this walk publishes goes through this family, so the
4877    /// carried-forward answers see each one exactly once: the field ownership
4878    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
4879    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
4880    fn add_declaration(
4881        &mut self,
4882        ancestry: &ParentIndex<'_>,
4883        code_unit: CodeUnit,
4884        node: Node<'_>,
4885        parent: Option<CodeUnit>,
4886        top_level: Option<CodeUnit>,
4887    ) {
4888        self.note_declaration(&code_unit);
4889        self.link_source_declaration(ancestry, &code_unit, node);
4890        let source = self.source;
4891        self.parsed
4892            .add_code_unit(code_unit, node, source, parent, top_level);
4893    }
4894
4895    /// Range-based form of [`Self::add_declaration`].
4896    fn add_declaration_with_range(
4897        &mut self,
4898        ancestry: &ParentIndex<'_>,
4899        code_unit: CodeUnit,
4900        range: Range,
4901        evidence: Option<Node<'_>>,
4902        parent: Option<CodeUnit>,
4903        top_level: Option<CodeUnit>,
4904    ) {
4905        self.note_declaration(&code_unit);
4906        self.link_source_range(ancestry, &code_unit, range, evidence);
4907        self.parsed
4908            .add_code_unit_with_range(code_unit, range, parent, top_level);
4909    }
4910
4911    /// Deferred-replacement form of [`Self::add_declaration`].
4912    fn replace_declaration_deferred(
4913        &mut self,
4914        ancestry: &ParentIndex<'_>,
4915        code_unit: CodeUnit,
4916        node: Node<'_>,
4917        parent: Option<CodeUnit>,
4918        top_level: Option<CodeUnit>,
4919    ) {
4920        self.note_replaced_declaration(&code_unit);
4921        self.link_source_declaration(ancestry, &code_unit, node);
4922        let source = self.source;
4923        self.parsed
4924            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
4925    }
4926
4927    /// Range-based form of [`Self::replace_declaration_deferred`].
4928    fn replace_declaration_with_range_deferred(
4929        &mut self,
4930        ancestry: &ParentIndex<'_>,
4931        code_unit: CodeUnit,
4932        range: Range,
4933        evidence: Option<Node<'_>>,
4934        parent: Option<CodeUnit>,
4935        top_level: Option<CodeUnit>,
4936    ) {
4937        self.note_replaced_declaration(&code_unit);
4938        self.link_source_range(ancestry, &code_unit, range, evidence);
4939        self.parsed
4940            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
4941    }
4942
4943    /// Notes one declaration about to enter the parse product.
4944    ///
4945    /// A declaration the product already holds is not a creation, so an open
4946    /// recovery capture ignores it -- which is the membership test the set
4947    /// difference it replaces performed. A creation inside a nested recovery
4948    /// belongs to the recoveries around it too, so every open capture takes it.
4949    fn note_declaration(&mut self, code_unit: &CodeUnit) {
4950        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
4951            for capture in &mut self.recovery_captures {
4952                if capture.removed_pre_existing.contains(code_unit) {
4953                    continue;
4954                }
4955                if capture.created_units.insert(code_unit.clone()) {
4956                    capture.created.push(code_unit.clone());
4957                }
4958            }
4959        }
4960        if let Some(field_owners) = self.field_owners.as_mut() {
4961            field_owners.record(code_unit, self.file);
4962        }
4963    }
4964
4965    /// Notes one declaration about to replace an existing one.
4966    ///
4967    /// A deferred replacement of a declaration that already owns children
4968    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
4969    /// a removal is the one thing the field index cannot absorb by addition.
4970    /// Drop it; the next question rebuilds it from the declarations that
4971    /// survive. A replacement of a unit with no children, and a "replacement"
4972    /// of a unit that is not there at all, remove nothing.
4973    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
4974        // Forward and definition occurrences remain real navigation alternatives.
4975        // Only identities removed with replaced children lose their mounts.
4976        for removed in self.declarations_a_replacement_removes(code_unit) {
4977            self.source_links.remove(&removed);
4978        }
4979        let removes_children = self.parsed.contains_declaration(code_unit)
4980            && self
4981                .parsed
4982                .children
4983                .get(code_unit)
4984                .is_some_and(|children| !children.is_empty());
4985        if removes_children {
4986            if !self.recovery_captures.is_empty() {
4987                let removed = self.declarations_a_replacement_removes(code_unit);
4988                for capture in &mut self.recovery_captures {
4989                    for unit in &removed {
4990                        // A declaration this capture watched being created is
4991                        // its own; one it did not is a declaration that was
4992                        // already there when the capture opened, so creating it
4993                        // again is a restoration and not a mint.
4994                        if !capture.created_units.contains(unit) {
4995                            capture.removed_pre_existing.insert(unit.clone());
4996                        }
4997                    }
4998                }
4999            }
5000            self.field_owners = None;
5001        }
5002        self.note_declaration(code_unit);
5003    }
5004
5005    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5006    /// when `code_unit` is replaced: its children, transitively.
5007    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5008        let mut removed = Vec::new();
5009        let mut seen = HashSet::default();
5010        let mut pending: Vec<CodeUnit> = self
5011            .parsed
5012            .children
5013            .get(code_unit)
5014            .cloned()
5015            .unwrap_or_default();
5016        while let Some(unit) = pending.pop() {
5017            if !seen.insert(unit.clone()) {
5018                continue;
5019            }
5020            if let Some(children) = self.parsed.children.get(&unit) {
5021                pending.extend(children.iter().cloned());
5022            }
5023            removed.push(unit);
5024        }
5025        removed
5026    }
5027
5028    fn visit_function_like_export_class_pair<'tree>(
5029        &mut self,
5030        node: Node<'tree>,
5031        scope: &ScopeInfo,
5032        stack: &mut Vec<CppWork<'tree>>,
5033        ancestry: &ParentIndex<'tree>,
5034    ) -> bool {
5035        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5036            return false;
5037        };
5038        let member_outcome = self
5039            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5040        // A malformed class body can escape into several following siblings
5041        // before the next export-macro class head appears. Inspect siblings in
5042        // order and stop at the first envelope that contains such a head. One
5043        // envelope can contain several following classes, all recovered in a
5044        // single bounded traversal.
5045        let mut displaced = node.next_named_sibling();
5046        while let Some(candidate) = displaced {
5047            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5048                break;
5049            }
5050            displaced = candidate.next_named_sibling();
5051        }
5052        let class_unit = self.visit_named_class_like_shape(
5053            node,
5054            recovered.name,
5055            // The adjacent initializer_list proves the class body envelope,
5056            // but its children are expression-shaped rather than declaration-
5057            // preserving. Index the class identity here; callable definitions
5058            // remain available from their ordinary out-of-line declarations.
5059            None,
5060            true,
5061            Some(recovered.range),
5062            recovered.raw_supertypes,
5063            recovered.base_facts,
5064            scope,
5065            stack,
5066            ancestry,
5067        );
5068        self.parsed
5069            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5070                recovery: recovered.range,
5071                unit: class_unit.clone(),
5072            });
5073        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5074            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
5075                tree.root_node(),
5076                class_unit.identifier(),
5077                self.source,
5078            )
5079        {
5080            self.visit_recovered_fragment_constructor(
5081                range,
5082                body,
5083                node,
5084                &class_unit,
5085                scope,
5086                ancestry,
5087            );
5088        }
5089        if let Some(outcome) = member_outcome {
5090            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5091        }
5092        self.consumed_fragment_regions
5093            .push((node.start_byte(), recovered.range.end_byte));
5094        true
5095    }
5096
5097    fn visit_embedded_function_like_export_classes<'tree>(
5098        &mut self,
5099        node: Node<'tree>,
5100        scope: &ScopeInfo,
5101        stack: &mut Vec<CppWork<'tree>>,
5102        ancestry: &ParentIndex<'tree>,
5103    ) -> bool {
5104        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5105        let found = !recovered_classes.is_empty();
5106        for recovered in recovered_classes {
5107            let member_outcome = self.reparse_fragmented_export_class_members(
5108                &recovered.fragmented_body,
5109                &recovered.name,
5110            );
5111            let class_unit = self.visit_named_class_like_shape(
5112                node,
5113                recovered.name,
5114                None,
5115                true,
5116                Some(recovered.range),
5117                Some(recovered.raw_supertypes),
5118                Some(recovered.base_facts),
5119                scope,
5120                stack,
5121                ancestry,
5122            );
5123            self.parsed
5124                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5125                    recovery: recovered.range,
5126                    unit: class_unit.clone(),
5127                });
5128            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5129                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
5130                    tree.root_node(),
5131                    class_unit.identifier(),
5132                    self.source,
5133                )
5134            {
5135                self.visit_recovered_fragment_constructor(
5136                    range,
5137                    body,
5138                    node,
5139                    &class_unit,
5140                    scope,
5141                    ancestry,
5142                );
5143            }
5144            if let Some(outcome) = member_outcome {
5145                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5146            }
5147        }
5148        found
5149    }
5150
5151    /// Walk `node`'s container, answering every ancestor question from
5152    /// `ancestry`.
5153    ///
5154    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5155    /// because one tree can be walked more than once -- a header's C and C++
5156    /// readings are the same tree under different tag semantics -- and the
5157    /// parent relation is a property of the tree, not of the reading.
5158    #[allow(clippy::too_many_arguments)]
5159    pub fn visit_container<'tree>(
5160        &mut self,
5161        node: Node<'tree>,
5162        ancestry: &ParentIndex<'tree>,
5163        package_name: &str,
5164        module: Option<CodeUnit>,
5165        class_unit: Option<CodeUnit>,
5166        template_signature: Option<String>,
5167        visible_using_namespaces: Vec<String>,
5168    ) {
5169        let scope = ScopeInfo {
5170            package_name: package_name.to_string(),
5171            module,
5172            class_unit,
5173            template_signature,
5174            template_metadata: None,
5175            declarations_are_fields: false,
5176            recovered_specialization_member_scope: false,
5177            visible_using_namespaces,
5178        };
5179        // The work loop dispatches a container's children, never the container
5180        // itself, so an `ERROR` container that is the seed does not reach the
5181        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5182        // breaks the parse leaves the whole translation unit as that seed
5183        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5184        // macro-error-class recovery reads.
5185        if node.is_error() {
5186            self.visit_object_macro_error_classes(node, &scope);
5187        }
5188        self.run_container_work(node, scope, ancestry);
5189        self.drain_partitioned_regions();
5190    }
5191
5192    /// Drain reparsed containers after their primary walk has indexed them.
5193    /// Recovered nodes retain their own tree and source context while they are
5194    /// visited; only then are their byte ranges marked consumed so a shared
5195    /// primary event stream does not visit the same declarations twice.
5196    fn drain_partitioned_regions(&mut self) {
5197        let mut consumed = Vec::new();
5198        while let Some(CppPartitionedRegion {
5199            tree,
5200            range,
5201            scope,
5202            recovery,
5203            source_context,
5204        }) = self.partitioned_regions.pop()
5205        {
5206            let root = tree.root_node();
5207            let container = root
5208                .descendant_for_byte_range(range.start, range.end)
5209                .expect("the queued container belongs to this tree");
5210            assert_eq!(container.byte_range(), range);
5211            let ancestry = self.recovery_ancestry(root);
5212            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5213                visitor.run_container_work(container, scope, &ancestry)
5214            });
5215            // A recovered walk may enqueue another partition inside this
5216            // region. Delay the outer claim until all nested regions have
5217            // drained, or their nodes would be skipped as already consumed.
5218            consumed.push((range.start, range.end));
5219        }
5220        self.consumed_fragment_regions.extend(consumed);
5221    }
5222
5223    /// Whether a work node lies entirely inside a byte region consumed by a
5224    /// fragmented export-class recovery (#938); such nodes were already indexed
5225    /// as members of the recovered class by the region reparse.
5226    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5227        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5228    }
5229
5230    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5231    /// candidate a recovery is about to reparse but has not yet turned into a
5232    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5233    /// prior structured recovery.
5234    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5235        self.consumed_fragment_regions
5236            .iter()
5237            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5238    }
5239
5240    /// Drive the container work loop from an explicit seed scope to completion. The
5241    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5242    /// stays alive for the whole traversal.
5243    ///
5244    /// Every ancestor question this walk asks is answered from `ancestry`, which
5245    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5246    /// node's position in the tree, which made a generated header with thousands
5247    /// of top-level declarations quadratic (#2361). The index is the caller's
5248    /// because it outlives any one walk: the file's tree is walked twice when a
5249    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5250    /// builds its own index for its own tree.
5251    fn run_container_work<'tree>(
5252        &mut self,
5253        node: Node<'tree>,
5254        scope: ScopeInfo,
5255        ancestry: &ParentIndex<'tree>,
5256    ) {
5257        self.drain_cpp_work(
5258            vec![CppWork::Container(CppContainer { node, scope })],
5259            ancestry,
5260        );
5261    }
5262
5263    /// The work loop itself, from whatever seed the caller built.
5264    ///
5265    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5266    /// that must walk only part of a reparsed tree seeds it with the sibling
5267    /// range it may walk instead, which keeps the `using namespace X;` scope
5268    /// accumulation `advance_cpp_siblings` performs.
5269    fn drain_cpp_work<'tree>(
5270        &mut self,
5271        mut stack: Vec<CppWork<'tree>>,
5272        ancestry: &ParentIndex<'tree>,
5273    ) {
5274        while let Some(work) = stack.pop() {
5275            match work {
5276                CppWork::Container(container) => {
5277                    push_cpp_container_work(container.node, container.scope, &mut stack);
5278                }
5279                CppWork::Siblings(siblings) => {
5280                    advance_cpp_siblings(siblings, self.source, &mut stack);
5281                }
5282                CppWork::Node(work) => {
5283                    if self.node_is_inside_consumed_fragment(work.node) {
5284                        continue;
5285                    }
5286                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5287                }
5288            }
5289        }
5290    }
5291
5292    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5293    /// it only when the entire region is member-shaped. This validation must happen
5294    /// before registering the recovered class because a rejected speculative range
5295    /// must not leak into the ordinary recovery path.
5296    fn reparse_fragmented_export_class_members(
5297        &self,
5298        fragmented: &FragmentedExportBody,
5299        class_name: &str,
5300    ) -> Option<FragmentedExportMembers> {
5301        if fragmented.reparse_start >= fragmented.reparse_end {
5302            return None;
5303        }
5304        let tree = cpp_reparse_fragmented_class_body(
5305            self.source,
5306            fragmented.reparse_start,
5307            fragmented.reparse_end,
5308        )?;
5309        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
5310            return Some(FragmentedExportMembers::Complete(tree));
5311        }
5312        let has_conditional_constructor = {
5313            let root = tree.root_node();
5314            let mut cursor = root.walk();
5315            root.named_children(&mut cursor).any(|child| {
5316                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5317            })
5318        };
5319        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5320    }
5321
5322    /// Index an already validated fragmented body as members of `class_unit`. The
5323    /// region reparse keeps each member's exact original byte and line positions.
5324    fn visit_fragmented_export_class_members(
5325        &mut self,
5326        origin: Node<'_>,
5327        outcome: FragmentedExportMembers,
5328        class_unit: CodeUnit,
5329        scope: &ScopeInfo,
5330    ) -> bool {
5331        self.with_recovery_source_context(origin, |visitor| {
5332            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5333        })
5334    }
5335
5336    fn visit_fragmented_export_class_members_in_context(
5337        &mut self,
5338        outcome: FragmentedExportMembers,
5339        class_unit: CodeUnit,
5340        scope: &ScopeInfo,
5341    ) -> bool {
5342        let (tree, complete) = match outcome {
5343            FragmentedExportMembers::Complete(tree) => (tree, true),
5344            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5345        };
5346        let root = tree.root_node();
5347        let class_name = class_unit.identifier().to_string();
5348        let member_scope = ScopeInfo {
5349            // A recovered export-macro class may borrow its namespace from an
5350            // earlier forward declaration even when the malformed node itself
5351            // sits at file scope. Use the recovered class identity as the
5352            // authoritative package for reparsed members as well.
5353            package_name: class_unit.package_name().to_string(),
5354            module: scope.module.clone(),
5355            class_unit: Some(class_unit),
5356            template_signature: scope.template_signature.clone(),
5357            template_metadata: None,
5358            declarations_are_fields: true,
5359            recovered_specialization_member_scope: false,
5360            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5361        };
5362        if !complete {
5363            // A conditional beginning immediately after an access label can
5364            // fragment one constructor declaration while leaving the rest of
5365            // the class body as unsafe statement soup. Recover only that
5366            // structurally proven constructor and leave the outer-tree
5367            // siblings unconsumed for their ordinary walk.
5368            let mut cursor = root.walk();
5369            let constructors = root
5370                .named_children(&mut cursor)
5371                .filter_map(|child| {
5372                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5373                })
5374                .collect::<Vec<_>>();
5375            // The reparsed region is its own tree, so this drain walks it with
5376            // its own parent index.
5377            let reparsed_ancestry = self.recovery_ancestry(root);
5378            for constructor in constructors {
5379                let mut stack = Vec::new();
5380                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
5381                while let Some(work) = stack.pop() {
5382                    match work {
5383                        CppWork::Container(container) => {
5384                            push_cpp_container_work(container.node, container.scope, &mut stack);
5385                        }
5386                        CppWork::Siblings(siblings) => {
5387                            advance_cpp_siblings(siblings, self.source, &mut stack);
5388                        }
5389                        CppWork::Node(work) => {
5390                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
5391                        }
5392                    }
5393                }
5394            }
5395            return false;
5396        }
5397        // The reparsed region is its own tree, so this walk indexes it itself.
5398        self.run_container_work(root, member_scope, &self.recovery_ancestry(root));
5399        true
5400    }
5401
5402    fn visit_recovered_fragment_constructor<'tree>(
5403        &mut self,
5404        range: std::ops::Range<usize>,
5405        constructor_body: Node<'tree>,
5406        class_declaration: Node<'tree>,
5407        class_unit: &CodeUnit,
5408        scope: &ScopeInfo,
5409        ancestry: &ParentIndex<'tree>,
5410    ) {
5411        self.with_recovery_source_context(class_declaration, |visitor| {
5412            visitor.visit_recovered_fragment_constructor_in_context(
5413                range,
5414                constructor_body,
5415                class_declaration,
5416                class_unit,
5417                scope,
5418                ancestry,
5419            );
5420        });
5421    }
5422
5423    fn visit_recovered_fragment_constructor_in_context<'tree>(
5424        &mut self,
5425        range: std::ops::Range<usize>,
5426        constructor_body: Node<'tree>,
5427        class_declaration: Node<'tree>,
5428        class_unit: &CodeUnit,
5429        scope: &ScopeInfo,
5430        ancestry: &ParentIndex<'tree>,
5431    ) {
5432        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
5433            return;
5434        };
5435        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
5436            tree.root_node(),
5437            range.start,
5438            class_unit.identifier(),
5439            self.source,
5440        ) else {
5441            return;
5442        };
5443        let member_scope = ScopeInfo {
5444            package_name: class_unit.package_name().to_string(),
5445            module: scope.module.clone(),
5446            class_unit: Some(class_unit.clone()),
5447            template_signature: scope.template_signature.clone(),
5448            template_metadata: None,
5449            declarations_are_fields: true,
5450            recovered_specialization_member_scope: false,
5451            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5452        };
5453        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
5454        else {
5455            return;
5456        };
5457        debug_assert_eq!(function.name, class_unit.identifier());
5458        let code_unit = function.code_unit(self.file.clone());
5459        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
5460        self.add_declaration_with_range(
5461            &recovered_ancestry,
5462            code_unit.clone(),
5463            Range {
5464                start_byte: function_declarator.start_byte(),
5465                end_byte: constructor_body.end_byte(),
5466                start_line: function_declarator.start_position().row + 1,
5467                end_line: constructor_body.end_position().row + 1,
5468            },
5469            Some(function_declarator),
5470            None,
5471            None,
5472        );
5473        let declaration = *self.source_links[&code_unit]
5474            .last()
5475            .expect("recovered constructor source");
5476        {
5477            let mut properties = self.source_properties.borrow_mut();
5478            let fact = properties
5479                .get_mut(&declaration)
5480                .expect("recovered constructor source fact");
5481            fact.occurrence_role =
5482                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
5483            fact.callable_is_constructor = true;
5484        }
5485        self.add_signature_with_metadata(
5486            code_unit.clone(),
5487            cpp_signature_metadata(
5488                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
5489                function_declarator,
5490                cpp_callable_linkage(class_declaration, self.source, ancestry),
5491                self.source,
5492                &recovered_ancestry,
5493            )
5494            .with_declaration_only(false),
5495        );
5496        self.parsed.add_child(class_unit.clone(), code_unit);
5497    }
5498
5499    fn visit_recovered_fragment_prefix_members<'tree>(
5500        &mut self,
5501        origin: Node<'_>,
5502        root: Node<'tree>,
5503        constructor_start: usize,
5504        class_unit: &CodeUnit,
5505        scope: &ScopeInfo,
5506        ancestry: &ParentIndex<'tree>,
5507    ) {
5508        self.with_recovery_source_context(origin, |visitor| {
5509            visitor.visit_recovered_fragment_prefix_members_in_context(
5510                root,
5511                constructor_start,
5512                class_unit,
5513                scope,
5514                ancestry,
5515            );
5516        });
5517    }
5518
5519    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
5520        &mut self,
5521        root: Node<'tree>,
5522        constructor_start: usize,
5523        class_unit: &CodeUnit,
5524        scope: &ScopeInfo,
5525        ancestry: &ParentIndex<'tree>,
5526    ) {
5527        let member_scope = ScopeInfo {
5528            package_name: class_unit.package_name().to_string(),
5529            module: scope.module.clone(),
5530            class_unit: Some(class_unit.clone()),
5531            template_signature: scope.template_signature.clone(),
5532            template_metadata: None,
5533            declarations_are_fields: true,
5534            recovered_specialization_member_scope: false,
5535            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5536        };
5537        let mut stack = vec![root];
5538        while let Some(current) = stack.pop() {
5539            if current.kind() == "comment" || current.start_byte() >= constructor_start {
5540                continue;
5541            }
5542            if current.end_byte() <= constructor_start
5543                && current.kind() != "translation_unit"
5544                && current.kind() != "labeled_statement"
5545                && current.kind() != "ERROR"
5546            {
5547                let mut work_stack = Vec::new();
5548                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
5549                while let Some(work) = work_stack.pop() {
5550                    match work {
5551                        CppWork::Container(container) => {
5552                            push_cpp_container_work(
5553                                container.node,
5554                                container.scope,
5555                                &mut work_stack,
5556                            );
5557                        }
5558                        CppWork::Siblings(siblings) => {
5559                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
5560                        }
5561                        CppWork::Node(work) => {
5562                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
5563                        }
5564                    }
5565                }
5566                continue;
5567            }
5568            if matches!(
5569                current.kind(),
5570                "translation_unit" | "labeled_statement" | "ERROR"
5571            ) {
5572                let mut cursor = current.walk();
5573                stack.extend(current.named_children(&mut cursor));
5574            }
5575        }
5576    }
5577
5578    /// Capture ancestors only when a proven recovery enters another tree. The
5579    /// ordinary declaration driver never performs this additional ancestor walk.
5580    fn with_recovery_source_context<T>(
5581        &mut self,
5582        node: Node<'_>,
5583        walk: impl FnOnce(&mut Self) -> T,
5584    ) -> T {
5585        use crate::graph::resolver::{
5586            enclosing_namespace_context, merge_preprocessor_guards,
5587            preprocessor_conditional_family_for_declaration, preprocessor_conditional_family_node,
5588            preprocessor_guard_environment,
5589        };
5590        let previous = self.active_source_context.take();
5591        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
5592        let inherited = (!primary).then_some(previous.as_ref()).flatten();
5593        let local_guards = preprocessor_guard_environment(node, self.source);
5594        let guards = match inherited {
5595            Some(outer) => outer
5596                .guards
5597                .as_ref()
5598                .zip(local_guards.as_ref())
5599                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
5600            None => local_guards,
5601        };
5602        let local_namespace = enclosing_namespace_context(node, self.source);
5603        let namespace = match (
5604            inherited.and_then(|outer| outer.child_namespace.as_ref()),
5605            local_namespace,
5606        ) {
5607            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5608            (Some(outer), None) => Some(outer.clone()),
5609            (None, local) => local,
5610        };
5611        let outer_lexical_scope =
5612            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
5613        let mut local_owners = Vec::new();
5614        let mut parent = node.parent();
5615        while let Some(ancestor) = parent {
5616            if matches!(
5617                ancestor.kind(),
5618                "namespace_definition"
5619                    | "class_specifier"
5620                    | "struct_specifier"
5621                    | "union_specifier"
5622                    | "enum_specifier"
5623            ) && let Some(name) = ancestor.child_by_field_name("name")
5624            {
5625                local_owners.push(node_text(name, self.source).to_owned());
5626            }
5627            parent = ancestor.parent();
5628        }
5629        local_owners.reverse();
5630        let mut child_lexical_scope = outer_lexical_scope.clone();
5631        child_lexical_scope.extend(local_owners);
5632        if matches!(
5633            node.kind(),
5634            "namespace_definition"
5635                | "class_specifier"
5636                | "struct_specifier"
5637                | "union_specifier"
5638                | "enum_specifier"
5639        ) && let Some(name) = node.child_by_field_name("name")
5640        {
5641            child_lexical_scope.push(node_text(name, self.source).to_owned());
5642        }
5643        let child_namespace = if node.kind() == "namespace_definition" {
5644            node.child_by_field_name("name")
5645                .map(|name| {
5646                    let local = node_text(name, self.source);
5647                    namespace
5648                        .as_ref()
5649                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
5650                })
5651                .or_else(|| namespace.clone())
5652        } else {
5653            namespace.clone()
5654        };
5655        let mut intern = |family: Node<'_>| {
5656            let mut collector = self.source_collector.borrow_mut();
5657            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5658                collector.intern_node(family)
5659            } else {
5660                collector
5661                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
5662            }
5663        };
5664        let family = preprocessor_conditional_family_node(node)
5665            .map(&mut intern)
5666            .or_else(|| inherited.and_then(|outer| outer.family));
5667        let exhaustive_family = preprocessor_conditional_family_for_declaration(node)
5668            .map(&mut intern)
5669            .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
5670        let local_completion =
5671            crate::graph::resolver::callable_guard_completion_byte(node, self.source);
5672        let callable_guard_completion_byte = match inherited {
5673            Some(outer) => local_completion
5674                .zip(outer.callable_guard_completion_byte)
5675                .map(|(local, outer)| local.max(outer)),
5676            None => local_completion,
5677        };
5678        self.active_source_context = Some(CppActiveSourceContext {
5679            guards,
5680            namespace: child_namespace.clone(),
5681            child_namespace,
5682            outer_lexical_scope: child_lexical_scope.clone(),
5683            child_lexical_scope,
5684            family,
5685            exhaustive_family,
5686            callable_guard_completion_byte,
5687        });
5688        let result = walk(self);
5689        self.active_source_context = previous;
5690        result
5691    }
5692
5693    fn visit_node<'tree>(
5694        &mut self,
5695        node: Node<'tree>,
5696        scope: &ScopeInfo,
5697        stack: &mut Vec<CppWork<'tree>>,
5698        ancestry: &ParentIndex<'tree>,
5699    ) {
5700        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
5701            self.visit_node(node, &recovered_scope, stack, ancestry);
5702            return;
5703        }
5704        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
5705            self.visit_node(node, &recovered_scope, stack, ancestry);
5706            return;
5707        }
5708        // Fragmented-class recovery below may consume a malformed function
5709        // envelope before the ordinary kind dispatch runs. Recover any
5710        // export-macro class embedded in that envelope first; the strict class
5711        // head/base/body predicate is independent of which later recovery owns
5712        // the surrounding parser fragment.
5713        if node.kind() == "function_definition" && node.has_error() {
5714            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
5715        }
5716        if let Some(FragmentedClassRecovery {
5717            declaration_node: class_node,
5718            name,
5719            raw_supertypes,
5720            body: fragmented,
5721        }) = fragmented_class_body(node, self.source)
5722        {
5723            let displaced_namespace_items =
5724                displaced_fragment_namespace_geometry(node, self.source)
5725                    .map(|boundary| boundary.namespace_items)
5726                    .unwrap_or_default();
5727            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
5728            let mut class_stack = Vec::new();
5729            // When the full body cannot be safely reparsed, the original class
5730            // node still proves ownership for its parser-visible prefix.
5731            let parser_visible_body =
5732                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
5733                    .then(|| cpp_body_node(class_node))
5734                    .flatten();
5735            let class_unit = self.visit_named_class_like_shape(
5736                class_node,
5737                name,
5738                parser_visible_body,
5739                true,
5740                Some(fragmented.class_range),
5741                Some(raw_supertypes),
5742                Some(extract_cpp_base_facts(class_node, self.source)),
5743                scope,
5744                &mut class_stack,
5745                ancestry,
5746            );
5747            let member_scope = ScopeInfo {
5748                package_name: class_unit.package_name().to_string(),
5749                module: scope.module.clone(),
5750                class_unit: Some(class_unit.clone()),
5751                template_signature: scope.template_signature.clone(),
5752                template_metadata: None,
5753                declarations_are_fields: true,
5754                recovered_specialization_member_scope: false,
5755                visible_using_namespaces: scope.visible_using_namespaces.clone(),
5756            };
5757            let complete = outcome.is_some_and(|outcome| {
5758                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
5759            });
5760            if complete {
5761                self.consumed_fragment_regions
5762                    .push((node.start_byte(), fragmented.class_range.end_byte));
5763            } else {
5764                // A macro-constrained member can make the full body reparse
5765                // unsafe while tree-sitter still exposes later class members
5766                // as bounded siblings up to the displaced `}`/`;`. Keep the
5767                // structurally proven class/base declaration and re-own those
5768                // sibling nodes under it. They retain their original parser
5769                // nodes and exact ranges; the close boundary comes solely from
5770                // `fragmented_class_body`.
5771                // Template wrappers put the escaped members beside the
5772                // template rather than beside its malformed declaration.
5773                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
5774                    if candidate.start_byte() >= fragmented.reparse_end {
5775                        break;
5776                    }
5777                    if cpp_fragment_sibling_is_class_member(
5778                        candidate,
5779                        fragmented.reparse_end,
5780                        self.source,
5781                    ) {
5782                        self.recovered_class_sibling_scopes
5783                            .insert(candidate.id(), member_scope.clone());
5784                    }
5785                }
5786            }
5787            for item in displaced_namespace_items {
5788                self.recovered_class_sibling_scopes
5789                    .insert(item.id(), scope.clone());
5790            }
5791            stack.extend(class_stack);
5792            return;
5793        }
5794        if self.visit_folded_aggregate(node, scope) {
5795            return;
5796        }
5797        match node.kind() {
5798            "template_declaration" => {
5799                if let Some(recovered) =
5800                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
5801                {
5802                    let mut template_scope = scope.clone();
5803                    template_scope.template_signature =
5804                        cpp_template_signature(node, recovered.declaration_node, self.source);
5805                    template_scope.template_metadata =
5806                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
5807                    let raw_supertypes =
5808                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
5809                    let mut class_stack = Vec::new();
5810                    let class_unit = self.visit_named_class_like_shape(
5811                        recovered.class_node,
5812                        recovered.name,
5813                        Some(recovered.body),
5814                        true,
5815                        Some(recovered.range),
5816                        raw_supertypes,
5817                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
5818                        &template_scope,
5819                        &mut class_stack,
5820                        ancestry,
5821                    );
5822                    self.parsed.record_materialization(
5823                        MaterializationRecord::RecoveredDeclaration {
5824                            recovery: recovered.range,
5825                            unit: class_unit.clone(),
5826                        },
5827                    );
5828                    let member_scope = ScopeInfo {
5829                        package_name: template_scope.package_name.clone(),
5830                        module: template_scope.module.clone(),
5831                        class_unit: Some(class_unit.clone()),
5832                        template_signature: template_scope.template_signature.clone(),
5833                        template_metadata: None,
5834                        declarations_are_fields: true,
5835                        recovered_specialization_member_scope: recovered
5836                            .class_node
5837                            .child_by_field_name("name")
5838                            .is_some_and(|name| name.kind() == "template_type"),
5839                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
5840                    };
5841                    for tail_member in recovered.tail_members.into_iter().rev() {
5842                        stack.push(CppWork::Node(CppNodeWork {
5843                            node: tail_member,
5844                            scope: member_scope.clone(),
5845                        }));
5846                    }
5847                    stack.extend(class_stack);
5848                    for sibling in recovered.member_siblings {
5849                        self.recovered_class_sibling_scopes
5850                            .insert(sibling.id(), member_scope.clone());
5851                    }
5852                    return;
5853                }
5854                for index in (0..node.named_child_count()).rev() {
5855                    let Some(child) = node.named_child(index) else {
5856                        continue;
5857                    };
5858                    if matches!(
5859                        child.kind(),
5860                        "class_specifier"
5861                            | "struct_specifier"
5862                            | "union_specifier"
5863                            | "enum_specifier"
5864                            | "function_definition"
5865                            | "declaration"
5866                            | "field_declaration"
5867                            | "alias_declaration"
5868                            | "namespace_definition"
5869                    ) {
5870                        let mut template_scope = scope.clone();
5871                        template_scope.template_signature =
5872                            cpp_template_signature(node, child, self.source);
5873                        template_scope.template_metadata =
5874                            cpp_template_metadata(node, child, self.source, ancestry);
5875                        if let Some(recovered) = recover_fragmented_partial_specialization(
5876                            node,
5877                            child,
5878                            self.source,
5879                            ancestry,
5880                        ) {
5881                            let code_unit = self.visit_named_class_like_shape(
5882                                recovered.declaration_node,
5883                                recovered.name,
5884                                None,
5885                                true,
5886                                Some(recovered.range),
5887                                None,
5888                                None,
5889                                &template_scope,
5890                                stack,
5891                                ancestry,
5892                            );
5893                            self.parsed.record_materialization(
5894                                MaterializationRecord::RecoveredDeclaration {
5895                                    recovery: recovered.range,
5896                                    unit: code_unit.clone(),
5897                                },
5898                            );
5899                            let mut member_scope = template_scope.clone();
5900                            member_scope.class_unit = Some(code_unit);
5901                            member_scope.declarations_are_fields = true;
5902                            member_scope.recovered_specialization_member_scope = true;
5903                            for prefix_member in recovered.prefix_members.into_iter().rev() {
5904                                stack.push(CppWork::Node(CppNodeWork {
5905                                    node: prefix_member,
5906                                    scope: member_scope.clone(),
5907                                }));
5908                            }
5909                            for sibling in recovered.member_siblings {
5910                                self.recovered_class_sibling_scopes
5911                                    .insert(sibling.id(), member_scope.clone());
5912                            }
5913                            for following in recovered.following_declarations.into_iter().rev() {
5914                                stack.push(CppWork::Node(CppNodeWork {
5915                                    node: following,
5916                                    scope: scope.clone(),
5917                                }));
5918                            }
5919                            return;
5920                        }
5921                        stack.push(CppWork::Node(CppNodeWork {
5922                            node: child,
5923                            scope: template_scope,
5924                        }));
5925                    }
5926                }
5927            }
5928            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
5929            "linkage_specification" => {
5930                if let Some(body) = cpp_body_node(node) {
5931                    stack.push(CppWork::Container(CppContainer {
5932                        node: body,
5933                        scope: scope.clone(),
5934                    }));
5935                } else {
5936                    stack.push(CppWork::Container(CppContainer {
5937                        node,
5938                        scope: scope.clone(),
5939                    }));
5940                }
5941            }
5942            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
5943                self.visit_class_like(node, scope, stack, ancestry)
5944            }
5945            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
5946            // A bare namespace-begin sentinel can make tree-sitter promote the
5947            // wrapped declaration to an ERROR node instead of the usual bogus
5948            // function_definition envelope. Keep the recovery entry point on
5949            // the same structured path for both shapes; ordinary ERROR nodes
5950            // retain their declaration-preserving wrapper traversal when the
5951            // sentinel predicate does not match.
5952            "ERROR" => {
5953                self.visit_object_macro_error_classes(node, scope);
5954                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
5955                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
5956                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5957                        return;
5958                    }
5959                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5960                        return;
5961                    }
5962                    self.visit_macro_swallowed_function_declarations(node, scope);
5963                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
5964                    self.visit_stranded_class_members(node, scope, ancestry);
5965                    stack.push(CppWork::Container(CppContainer {
5966                        node,
5967                        scope: scope.clone(),
5968                    }));
5969                }
5970            }
5971            "declaration" => {
5972                if node.has_error() {
5973                    // Object-like macro lines before a function-like
5974                    // export-macro class head keep the head in a `declaration`
5975                    // rather than in an `ERROR` (#2924). It is the same head
5976                    // and the same following-sibling body, so it takes the same
5977                    // recovery; without it the head's macro invocation indexes
5978                    // as a field named after the macro.
5979                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
5980                        return;
5981                    }
5982                    self.visit_prototype_macro_declarations(node, scope);
5983                    if self.node_is_inside_consumed_fragment(node) {
5984                        // The whole declaration was a recovered K&R
5985                        // prototype macro invocation (issue #2932); the
5986                        // reparse above already indexed its Function.
5987                        // Falling through to the ordinary declaration
5988                        // visitor would additionally mint the swapped-field
5989                        // wreckage this recovery exists to replace.
5990                        return;
5991                    }
5992                }
5993                if scope.class_unit.is_some()
5994                    && scope.declarations_are_fields
5995                    && scope.recovered_specialization_member_scope
5996                    && let Some(alias_name) =
5997                        recovered_using_declaration_alias_name(node, self.source)
5998                {
5999                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6000                } else {
6001                    self.visit_declaration(
6002                        node,
6003                        scope,
6004                        scope.declarations_are_fields,
6005                        stack,
6006                        ancestry,
6007                    )
6008                }
6009            }
6010            // A K&R prototype macro invocation with a pointer return type and a
6011            // simple argument list parses with no ERROR node at all: tree-sitter
6012            // reads `T *name _(( args ));` as a multiplication of a type by a
6013            // call (`T * name_(...)`), wrapped in an `expression_statement`
6014            // that `has_error()` only because of a `MISSING "::"` inside it
6015            // (issue #2932). Nothing else mints a declaration from an
6016            // `expression_statement` at declaration scope today, so there is
6017            // no existing behavior to preserve here if this node is not the
6018            // K&R shape; the admission gate inside
6019            // `visit_prototype_macro_declarations` (exactly one clean function
6020            // prototype spanning the recovered run) is what keeps this arm
6021            // safe on an ordinary errorful expression statement that happens
6022            // to contain nested parentheses.
6023            "expression_statement" => {
6024                if node.has_error() {
6025                    self.visit_prototype_macro_declarations(node, scope);
6026                }
6027            }
6028            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
6029            "preproc_call" => self.visit_preproc_call(node, scope),
6030            "type_definition" | "alias_declaration" => {
6031                self.visit_type_declaration(node, scope, stack, ancestry)
6032            }
6033            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6034            // `#include` is collected by the coordinated primary events before the
6035            // walk, so a directive the container walk never reaches -- inside
6036            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6037            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6038            // include claim.
6039            "preproc_include" => {}
6040            kind if preserves_declaration_scope_through_wrapper(
6041                kind,
6042                scope.class_unit.is_some(),
6043            ) =>
6044            {
6045                // A preprocessor conditional gates every declaration inside it
6046                // on a configuration this analyzer never evaluates; record the
6047                // interval so declaration state can say so (issue #1476). The
6048                // else/elif branches are children of the `preproc_if` node, so
6049                // recording the openers covers every branch.
6050                if kind == "labeled_statement" {
6051                    self.visit_access_label_constructor(node, scope);
6052                }
6053                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6054                    let mut range = cpp_declaration_range(node);
6055                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6056                        range.end_byte = boundary.end_byte;
6057                        range.end_line = boundary.end_line;
6058                    }
6059                    self.parsed.record_materialization(
6060                        MaterializationRecord::ConfigurationConditional { range },
6061                    );
6062                    if node.has_error() {
6063                        // A malformed export-macro class can close the namespace
6064                        // node early while the enclosing include guard still owns
6065                        // the remaining class-head/body pairs. The ordinary walk
6066                        // cannot carry the lost namespace through those promoted
6067                        // siblings. Scan only structured ERROR nodes in this
6068                        // already-malformed conditional; the pair recovery's
6069                        // exact class/macro/body predicate remains the admission
6070                        // gate, and its namespace lifting restores the owner.
6071                        let mut candidates = vec![node];
6072                        while let Some(candidate) = candidates.pop() {
6073                            // An `ERROR` still inside a namespace body is one
6074                            // the ordinary walk reaches with that namespace in
6075                            // scope. Claiming it here registers the recovered
6076                            // class at file scope and the consumed region then
6077                            // suppresses the walk that would have named it
6078                            // correctly -- which is why Botan's `DL_Group` was
6079                            // `DL_Group` and not `Botan.DL_Group` in the real
6080                            // header, where an include guard wraps the
6081                            // namespace, and was right in a fixture without one
6082                            // (#2552).
6083                            if candidate.kind() == "ERROR"
6084                                && !cpp_is_inside_namespace_body(candidate, ancestry)
6085                                && self.visit_function_like_export_class_pair(
6086                                    candidate, scope, stack, ancestry,
6087                                )
6088                            {
6089                                continue;
6090                            }
6091                            for index in (0..candidate.named_child_count()).rev() {
6092                                candidates.push(
6093                                    candidate
6094                                        .named_child(index)
6095                                        .expect("index below the node's own named child count"),
6096                                );
6097                            }
6098                        }
6099                    }
6100                }
6101                stack.push(CppWork::Container(CppContainer {
6102                    node,
6103                    scope: scope.clone(),
6104                }))
6105            }
6106            // A macro invocation the parser could not group leaves its head in
6107            // whatever slot was free. The `function_definition` and `ERROR`
6108            // arms above try the same recovery on the envelopes they get; a
6109            // flattened run leaves a bare `identifier`/`type_identifier`, and a
6110            // swallowed tail can land in a `parameter_declaration` (#3094).
6111            _ => {
6112                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6113            }
6114        }
6115    }
6116
6117    fn visit_macro_swallowed_function_declarations<'tree>(
6118        &mut self,
6119        envelope: Node<'tree>,
6120        scope: &ScopeInfo,
6121    ) {
6122        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
6123            || envelope.kind() == "ERROR"
6124                && envelope
6125                    .parent()
6126                    .is_some_and(|parent| parent.kind() == "ERROR")
6127        {
6128            return;
6129        }
6130        let mut stack = (0..envelope.named_child_count())
6131            .filter_map(|index| envelope.named_child(index))
6132            .collect::<Vec<_>>();
6133        while let Some(node) = stack.pop() {
6134            if node.kind() == "function_declarator" {
6135                self.visit_error_swallowed_function_declaration(node, scope);
6136            }
6137            for child in named_children_iter(node) {
6138                stack.push(child);
6139            }
6140        }
6141    }
6142
6143    /// Index the declarations an attribute-like macro invocation swallowed into
6144    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
6145    /// the shape and why the parser produces it.
6146    fn visit_macro_wrapped_declarations<'tree>(
6147        &mut self,
6148        envelope: Node<'tree>,
6149        scope: &ScopeInfo,
6150        ancestry: &ParentIndex<'tree>,
6151    ) {
6152        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
6153        if recovered.is_empty() {
6154            return;
6155        }
6156        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
6157        self.record_recovered_declarations(recovery, envelope, |visitor| {
6158            for declaration in recovered {
6159                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
6160            }
6161        });
6162    }
6163
6164    /// Index the declarations a macro invocation collapsed. See
6165    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
6166    /// produces it. Returns whether it claimed the region beginning at
6167    /// `envelope`.
6168    ///
6169    /// The parser's own nodes cannot be read the way the swallowed tail of the
6170    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
6171    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
6172    /// with no declarator left at all, and the declarators that do survive on
6173    /// the envelope's declarator spine pair one declaration's name with the
6174    /// *next* declaration's parameter list. Reading those would be a guess.
6175    ///
6176    /// The bytes are still ordinary declarations, though, and the collapse is
6177    /// the parser carrying the failure forward from one macro invocation. So
6178    /// reparse the region, walk the items the parser makes of it, and when one
6179    /// item is itself a collapsed run, recover that invocation from its own
6180    /// bytes and resume the scan just past it. Each pass starts later than the
6181    /// last, so the scan is a loop over the invocations that collapse, not over
6182    /// the declarations: `llama.h` needs three passes for 214 declarations.
6183    ///
6184    /// The region can reach past `envelope` into the siblings the parser handed
6185    /// the rest of one invocation, so it is recorded as consumed: those
6186    /// siblings are this recovery's, and the ordinary walk must not read them
6187    /// again. Recording it after the scan, not before, keeps the scan's own
6188    /// reparsed nodes -- which carry their original byte offsets -- visible to
6189    /// the walk it drives.
6190    fn visit_collapsed_macro_declaration_run<'tree>(
6191        &mut self,
6192        envelope: Node<'tree>,
6193        scope: &ScopeInfo,
6194        ancestry: &ParentIndex<'tree>,
6195    ) -> bool {
6196        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
6197            return false;
6198        };
6199        let start = envelope.start_byte();
6200        let end = run.region_end;
6201        let recovery = cpp_recovery_window(self.source, start, end);
6202        self.record_recovered_declarations(recovery, envelope, |visitor| {
6203            let mut position = start;
6204            while position < end {
6205                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
6206                    return;
6207                };
6208                let root = tree.root_node();
6209                // A region reparse is its own tree and needs its own parent
6210                // index and dependency arena; the caller's answers nothing
6211                // about these nodes.
6212                let ancestry = visitor.recovery_ancestry(root);
6213                let mut cursor = root.walk();
6214                let collapsed =
6215                    root.named_children(&mut cursor)
6216                        .enumerate()
6217                        .find_map(|(index, item)| {
6218                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
6219                                .map(|run| (index, item, run))
6220                        });
6221                // Walk only the items before the collapsed one. It swallowed
6222                // the rest of the region, so handing it to the ordinary walk
6223                // would re-enter this recovery on a region that starts where
6224                // this one did.
6225                let mut stack = Vec::new();
6226                push_cpp_sibling_range(
6227                    root,
6228                    0,
6229                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
6230                    scope.clone(),
6231                    &mut stack,
6232                );
6233                visitor.drain_cpp_work(stack, &ancestry);
6234                let Some((_, item, run)) = collapsed else {
6235                    return;
6236                };
6237                // On its own bytes the invocation is the declaration-scope
6238                // shape `macro_wrapped_declarations` reads, so its wrapped
6239                // declaration needs no reader of its own here.
6240                if let Some(head) =
6241                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
6242                {
6243                    let head_root = head.root_node();
6244                    let head_ancestry = visitor.recovery_ancestry(head_root);
6245                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
6246                }
6247                assert!(
6248                    run.invocation_end > position,
6249                    "a collapsed run at {position} must end after the byte the scan resumed \
6250                     from, but ended at {}",
6251                    run.invocation_end
6252                );
6253                position = run.invocation_end;
6254            }
6255        });
6256        self.consumed_fragment_regions.push((start, end));
6257        true
6258    }
6259
6260    /// Index the members a string-argument attribute macro stranded in an
6261    /// `ERROR` inside a class body.
6262    ///
6263    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
6264    /// grouping of the attributed member and of every member written after it
6265    /// until it recovers. The members are still whole `function_declarator`
6266    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
6267    /// export-macro class such as Botan's `DL_Group` was indexed with no
6268    /// members at all (#2552).
6269    fn visit_stranded_class_members<'tree>(
6270        &mut self,
6271        node: Node<'tree>,
6272        scope: &ScopeInfo,
6273        ancestry: &ParentIndex<'tree>,
6274    ) {
6275        if scope.class_unit.is_none() || !scope.declarations_are_fields {
6276            return;
6277        }
6278        for member in stranded_declaration_run(node, self.source).declarations {
6279            self.add_macro_wrapped_declaration(member, scope, ancestry);
6280        }
6281    }
6282
6283    /// Index the constructor an access label swallowed in a reparsed class
6284    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
6285    ///
6286    /// The declarator is recovered by reparsing from that call to the end of
6287    /// the label's own statement, which is the same offset-preserving region
6288    /// reparse every other recovery here uses, so the recovered nodes carry
6289    /// their true source positions.
6290    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6291        let Some(class_unit) = scope.class_unit.clone() else {
6292            return;
6293        };
6294        if !scope.declarations_are_fields {
6295            return;
6296        }
6297        let class_name = class_unit.identifier().to_string();
6298        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
6299        else {
6300            return;
6301        };
6302        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
6303            return;
6304        };
6305        let root = tree.root_node();
6306        let Some(declarator) =
6307            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
6308        else {
6309            return;
6310        };
6311        let reparsed_ancestry = self.recovery_ancestry(root);
6312        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
6313        let range = cpp_declaration_range(definition.unwrap_or(declarator));
6314        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
6315        self.record_recovered_declarations(recovery, node, |visitor| {
6316            visitor.add_macro_wrapped_declaration(
6317                MacroWrappedDeclaration {
6318                    declarator,
6319                    range,
6320                    is_static: false,
6321                },
6322                scope,
6323                &reparsed_ancestry,
6324            );
6325        });
6326    }
6327
6328    fn add_macro_wrapped_declaration<'tree>(
6329        &mut self,
6330        declaration: MacroWrappedDeclaration<'tree>,
6331        scope: &ScopeInfo,
6332        ancestry: &ParentIndex<'tree>,
6333    ) {
6334        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
6335        else {
6336            return;
6337        };
6338        let code_unit =
6339            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
6340        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
6341        let is_constructor = scope
6342            .class_unit
6343            .as_ref()
6344            .is_some_and(|class| function.name == class.identifier())
6345            || cpp_callable_is_structural_constructor(
6346                declaration.declarator,
6347                self.source,
6348                ancestry,
6349            );
6350        let already_declared = self.parsed.contains_declaration(&code_unit);
6351        if already_declared {
6352            self.parsed
6353                .record_navigation_range(code_unit.clone(), declaration.range);
6354            if definition.is_none() {
6355                return;
6356            }
6357            // Preserve the earlier declaration-only occurrence while adding
6358            // the recovered body as a distinct definition occurrence.
6359        }
6360        self.add_declaration_with_range(
6361            ancestry,
6362            code_unit.clone(),
6363            declaration.range,
6364            Some(declaration.declarator),
6365            None,
6366            None,
6367        );
6368        if definition.is_some() {
6369            let recovered_declaration = *self
6370                .source_links
6371                .get(&code_unit)
6372                .and_then(|links| links.last())
6373                .expect("recovered definition source");
6374            let mut properties = self.source_properties.borrow_mut();
6375            let fact = properties
6376                .get_mut(&recovered_declaration)
6377                .expect("recovered definition source fact");
6378            fact.occurrence_role =
6379                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6380            fact.callable_is_constructor |= is_constructor;
6381        }
6382        let signature = normalize_cpp_whitespace(
6383            self.source
6384                .get(declaration.range.start_byte..declaration.range.end_byte)
6385                .expect("a recovered declaration range covers one source range"),
6386        );
6387        let linkage = if declaration.is_static {
6388            CallableLinkage::Internal
6389        } else {
6390            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
6391        };
6392        // Whether this is a definition is a property of the recovered node, not
6393        // of the caller: a declarator that a `function_definition` gives a body
6394        // is a definition wherever the recovery found it.
6395        let declaration_only = definition.is_none();
6396        self.add_signature_with_metadata(
6397            code_unit.clone(),
6398            cpp_signature_metadata(
6399                signature,
6400                declaration.declarator,
6401                linkage,
6402                self.source,
6403                ancestry,
6404            )
6405            .with_declaration_only(declaration_only),
6406        );
6407        if already_declared {
6408            return;
6409        }
6410        if let Some(parent) = &scope.class_unit {
6411            self.parsed.add_child(parent.clone(), code_unit);
6412        } else if let Some(module) = &scope.module {
6413            self.parsed.add_child(module.clone(), code_unit);
6414        }
6415    }
6416
6417    /// Index only anonymous aggregate declarations from an ordinary
6418    /// function body. Function bodies are otherwise outside the declaration
6419    /// walk, but their anonymous aggregate owners must exist so structured
6420    /// local binding inference can name their fields (#2994).
6421    fn visit_anonymous_local_aggregates_in_function<'tree>(
6422        &mut self,
6423        function: Node<'tree>,
6424        scope: &ScopeInfo,
6425        stack: &mut Vec<CppWork<'tree>>,
6426        ancestry: &ParentIndex<'tree>,
6427    ) {
6428        let Some(body) = cpp_body_node(function) else {
6429            return;
6430        };
6431        // A body opens a block scope, so nothing declared in it is a member of
6432        // the class the definition may be written inside. Dropping the
6433        // aggregate carrier gives an inline member function's body the same
6434        // block scope an out-of-line `void C::f()` body already has; without
6435        // it a C++ header, where member bodies are usually inline, would keep
6436        // none of these owners (#3301).
6437        let block_scope;
6438        let scope = if scope.class_unit.is_some() {
6439            block_scope = ScopeInfo {
6440                class_unit: None,
6441                declarations_are_fields: false,
6442                ..scope.clone()
6443            };
6444            &block_scope
6445        } else {
6446            scope
6447        };
6448        let mut pending = vec![body];
6449        while let Some(node) = pending.pop() {
6450            if matches!(node.kind(), "function_definition" | "lambda_expression") {
6451                continue;
6452            }
6453            if node.kind() == "declaration"
6454                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
6455            {
6456                continue;
6457            }
6458            if matches!(
6459                node.kind(),
6460                "class_specifier" | "struct_specifier" | "union_specifier"
6461            ) {
6462                continue;
6463            }
6464            let mut cursor = node.walk();
6465            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
6466            children.reverse();
6467            pending.extend(children);
6468        }
6469    }
6470
6471    fn visit_error_swallowed_function_declaration<'tree>(
6472        &mut self,
6473        node: Node<'tree>,
6474        scope: &ScopeInfo,
6475    ) -> bool {
6476        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
6477            return false;
6478        };
6479        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6480            return false;
6481        };
6482        let root = tree.root_node();
6483        let mut cursor = root.walk();
6484        let declarations = root
6485            .named_children(&mut cursor)
6486            .filter(|child| child.kind() != "comment")
6487            .collect::<Vec<_>>();
6488        let [declaration] = declarations.as_slice() else {
6489            return false;
6490        };
6491        if declaration.kind() != "declaration"
6492            || declaration.has_error()
6493            || declaration.start_byte() != start
6494            || declaration.end_byte() != end
6495        {
6496            return false;
6497        }
6498        let recovery = cpp_recovery_window(self.source, start, end);
6499        // The reparsed region is its own tree, so this walk indexes it itself.
6500        let reparsed_ancestry = self.recovery_ancestry(root);
6501        self.record_recovered_declarations(recovery, node, |visitor| {
6502            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6503        });
6504        true
6505    }
6506
6507    /// Recover a K&R-compatibility prototype macro invocation such as
6508    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
6509    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
6510    /// defined only in headers outside this workspace (ruby's own
6511    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
6512    /// `name _((args))` and turns each such line into a malformed
6513    /// `declaration` or pointer-expression statement. Before this
6514    /// recovery, the ordinary declaration visitor read that wreckage as a
6515    /// field with the type and name swapped (e.g. a field literally named
6516    /// `VALUE`) and indexed no prototype for the real name at all.
6517    ///
6518    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
6519    /// preserve the declared name, a known one-argument compatibility macro,
6520    /// the nested argument list, and a live terminator. It rejects arbitrary
6521    /// identifiers and bare `ERROR` envelopes that flattened those relations;
6522    /// guessing there previously turned malformed non-macro declarations into
6523    /// invented functions. For each proven candidate this reparses the range
6524    /// before the macro token, the inner parenthesized argument node, and the
6525    /// terminating `;` as one included-range parse. That is the parser-native
6526    /// equivalent of expanding `#define _(args) args` while retaining original
6527    /// byte offsets. The result is admitted only when it is exactly one clean
6528    /// function prototype spanning the recovered run.
6529    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6530        for candidate in cpp_prototype_macro_candidates(node, self.source) {
6531            let start = candidate.run_start;
6532            let end = candidate.semicolon_end;
6533            if self.byte_range_is_inside_consumed_fragment(start, end) {
6534                continue;
6535            }
6536            let Some(tree) = parse_source_ranges_with_cancellation(
6537                &tree_sitter_cpp::LANGUAGE.into(),
6538                self.source,
6539                &candidate.ranges(),
6540                None,
6541            ) else {
6542                continue;
6543            };
6544            let root = tree.root_node();
6545            let mut cursor = root.walk();
6546            let declarations = root
6547                .named_children(&mut cursor)
6548                .filter(|child| child.kind() != "comment")
6549                .collect::<Vec<_>>();
6550            let [declaration] = declarations.as_slice() else {
6551                continue;
6552            };
6553            if declaration.kind() != "declaration"
6554                || declaration.has_error()
6555                || declaration.start_byte() != start
6556                || declaration.end_byte() != end
6557                || declaration
6558                    .child_by_field_name("declarator")
6559                    .and_then(extract_function_declarator)
6560                    .is_none()
6561            {
6562                continue;
6563            }
6564            let recovery = cpp_recovery_window(self.source, start, end);
6565            // The reparsed region is its own tree, so this walk indexes it
6566            // itself.
6567            let reparsed_ancestry = self.recovery_ancestry(root);
6568            self.record_recovered_declarations(recovery, node, |visitor| {
6569                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6570            });
6571            self.consumed_fragment_regions.push((start, end));
6572        }
6573    }
6574
6575    /// Declare one Module per namespace level of `levels` under `package_name`,
6576    /// each recording the range of the construct that writes it, and return the
6577    /// innermost level's package name and Module. A level an earlier definition
6578    /// already declared is reused, which is also how a reopened namespace keeps
6579    /// the range of the definition that first declared it.
6580    fn declare_namespace_levels(
6581        &mut self,
6582        mut package_name: String,
6583        levels: Vec<(String, Range)>,
6584        node: Node<'_>,
6585        ancestry: &ParentIndex<'_>,
6586    ) -> (String, Option<CodeUnit>) {
6587        let mut module = None;
6588        for (component, range) in levels {
6589            let full_name = if package_name.is_empty() {
6590                component
6591            } else {
6592                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
6593            };
6594            let level = CodeUnit::new_fq(
6595                self.file.clone(),
6596                CodeUnitType::Module,
6597                "",
6598                full_name.clone(),
6599                cpp_namespace_fq(&full_name),
6600            );
6601            if !self.parsed.contains_declaration(&level) {
6602                self.add_declaration_with_range(
6603                    ancestry,
6604                    level.clone(),
6605                    range,
6606                    Some(node),
6607                    None,
6608                    None,
6609                );
6610            }
6611            package_name = full_name;
6612            module = Some(level);
6613        }
6614        (package_name, module)
6615    }
6616
6617    /// Restore the complete lexical namespace path when recovery dropped an
6618    /// ancestor or retained it past its real close. Parsed namespaces beginning
6619    /// inside the recovered region still contribute their normal nesting.
6620    /// Crossing that boundary also clears any malformed class owner.
6621    fn recovered_namespace_scope(
6622        &mut self,
6623        node: Node<'_>,
6624        scope: &ScopeInfo,
6625    ) -> Option<ScopeInfo> {
6626        self.orphaned_namespaces.region_at(node.start_byte())?;
6627        // Each level carries the `namespace Name { ... }` construct that writes
6628        // it. Declaring them at `node` instead recorded the range of whichever
6629        // member the walk happened to reach first, so a namespace whose head
6630        // collapsed into an ERROR reported its first inner declaration's lines
6631        // as its own (#3309).
6632        let levels = self
6633            .orphaned_namespaces
6634            .enclosing_namespace_levels(node, self.source);
6635        let package_name = levels
6636            .iter()
6637            .map(|(component, _)| component.as_str())
6638            .collect::<Vec<_>>()
6639            .join(CPP_PACKAGE_SEPARATOR);
6640        if package_name == scope.package_name {
6641            return None;
6642        }
6643        let (package_name, module) =
6644            self.declare_namespace_levels(String::new(), levels, node, self.primary_ancestry);
6645        Some(ScopeInfo {
6646            package_name,
6647            module,
6648            // A recovered namespace body is no longer inside the class (or
6649            // function) that tree-sitter accidentally used as its wrapper.
6650            // Keep namespace/module context, but never carry that owner into
6651            // declarations published from the recovered region.
6652            class_unit: None,
6653            template_signature: None,
6654            template_metadata: None,
6655            declarations_are_fields: false,
6656            recovered_specialization_member_scope: false,
6657            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6658        })
6659    }
6660
6661    fn visit_namespace<'tree>(
6662        &mut self,
6663        node: Node<'tree>,
6664        scope: &ScopeInfo,
6665        stack: &mut Vec<CppWork<'tree>>,
6666        ancestry: &ParentIndex<'tree>,
6667    ) {
6668        let name_node = node.child_by_field_name("name");
6669        let Some(name_node) = name_node else {
6670            if let Some(body) = cpp_body_node(node) {
6671                stack.push(CppWork::Container(CppContainer {
6672                    node: body,
6673                    scope: scope.clone(),
6674                }));
6675            }
6676            return;
6677        };
6678        // Diagnostic corpora contain deliberately ill-formed global namespace
6679        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
6680        // the leading global `::` as the first anonymous child. Honor that AST
6681        // boundary instead of appending the name to the lexical namespace;
6682        // appending produced legacy names such as `outer::::outer::inner`, which
6683        // could not round-trip through the structured FqName boundary.
6684        let explicitly_global = name_node
6685            .child(0)
6686            .is_some_and(|child| !child.is_named() && child.kind() == "::");
6687        let components = cpp_namespace_name_components(name_node, self.source);
6688        if components.is_empty() {
6689            return;
6690        }
6691        // One Module per namespace level. C++17's `namespace a::b { ... }` is
6692        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
6693        // shorthand must declare `a` as well as `a::b` -- extracting only the
6694        // innermost level left the enclosing namespace undeclared and made the
6695        // two spellings of one construct disagree (issue #1878).
6696        let package_name = if explicitly_global {
6697            String::new()
6698        } else {
6699            scope.package_name.clone()
6700        };
6701        let definition = node_range(node);
6702        let (package_name, module) = self.declare_namespace_levels(
6703            package_name,
6704            components
6705                .into_iter()
6706                .map(|component| (component, definition))
6707                .collect(),
6708            node,
6709            ancestry,
6710        );
6711
6712        let namespace_scope = ScopeInfo {
6713            package_name,
6714            module,
6715            // C++ never nests a namespace inside a class, so a surviving
6716            // class_unit here is always recovery bleed: a malformed-region
6717            // boundary upstream mis-scoped this namespace block. Keeping the
6718            // owner would mint the namespace's declarations as class members
6719            // under a re-appended package, desyncing the fq boundary assert
6720            // (#2306). Dropping it is identity-neutral for valid code, where
6721            // class_unit is always empty at a namespace definition.
6722            class_unit: None,
6723            template_signature: scope.template_signature.clone(),
6724            template_metadata: scope.template_metadata.clone(),
6725            declarations_are_fields: false,
6726            recovered_specialization_member_scope: false,
6727            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6728        };
6729        let container = cpp_body_node(node).unwrap_or(node);
6730        // A malformed export-macro class body may turn the following class
6731        // into a descendant of a bogus function/labeled/error envelope. Those
6732        // descendants are not declaration containers and the ordinary walk
6733        // intentionally does not descend into them. Scan the namespace tree
6734        // once for the strict embedded class geometry before scheduling its
6735        // normal declarations. When one envelope matches, its helper recovers
6736        // every embedded class and the walk need not inspect its descendants.
6737        let mut candidates = vec![container];
6738        while let Some(candidate) = candidates.pop() {
6739            if matches!(
6740                candidate.kind(),
6741                "ERROR" | "function_definition" | "labeled_statement"
6742            ) && self.visit_embedded_function_like_export_classes(
6743                candidate,
6744                &namespace_scope,
6745                stack,
6746                ancestry,
6747            ) {
6748                continue;
6749            }
6750            for index in (0..candidate.named_child_count()).rev() {
6751                candidates.push(
6752                    candidate
6753                        .named_child(index)
6754                        .expect("index below the node's own named child count"),
6755                );
6756            }
6757        }
6758        stack.push(CppWork::Container(CppContainer {
6759            node: container,
6760            scope: namespace_scope,
6761        }));
6762    }
6763
6764    fn visit_class_like<'tree>(
6765        &mut self,
6766        node: Node<'tree>,
6767        scope: &ScopeInfo,
6768        stack: &mut Vec<CppWork<'tree>>,
6769        ancestry: &ParentIndex<'tree>,
6770    ) {
6771        let Some(name) = class_like_name(node, self.source, ancestry) else {
6772            return;
6773        };
6774        let name = qualified_class_name_chain(node, self.source, scope)
6775            .map(|chain| chain.join("$"))
6776            .unwrap_or(name);
6777        self.visit_named_class_like(node, name, scope, stack, ancestry);
6778    }
6779
6780    fn visit_named_class_like<'tree>(
6781        &mut self,
6782        node: Node<'tree>,
6783        name: String,
6784        scope: &ScopeInfo,
6785        stack: &mut Vec<CppWork<'tree>>,
6786        ancestry: &ParentIndex<'tree>,
6787    ) {
6788        let body = cpp_body_node(node);
6789        let definition_body_present = body.is_some();
6790        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
6791            .then(|| extract_cpp_supertypes(node, self.source));
6792        self.visit_named_class_like_shape(
6793            node,
6794            name,
6795            body,
6796            definition_body_present,
6797            None,
6798            raw_supertypes,
6799            Some(extract_cpp_base_facts(node, self.source)),
6800            scope,
6801            stack,
6802            ancestry,
6803        );
6804    }
6805
6806    /// Whether this class-like declaration is a C tag that belongs to the
6807    /// enclosing non-aggregate scope rather than to the aggregate it is
6808    /// lexically written inside.
6809    ///
6810    /// `class_specifier` is deliberately excluded: `class` is not C, so text
6811    /// that spells one in a `.c` file is not C code and keeps the C++ reading
6812    /// rather than getting a half-C identity.
6813    fn mints_tag_at_enclosing_c_scope(
6814        &self,
6815        declaration_node: Node<'_>,
6816        scope: &ScopeInfo,
6817        ancestry: &ParentIndex<'_>,
6818    ) -> bool {
6819        scope.class_unit.is_some()
6820            && matches!(
6821                declaration_node.kind(),
6822                "struct_specifier" | "union_specifier" | "enum_specifier"
6823            )
6824            && class_like_name(declaration_node, self.source, ancestry).is_some()
6825    }
6826
6827    #[allow(clippy::too_many_arguments)]
6828    fn visit_named_class_like_shape<'tree>(
6829        &mut self,
6830        declaration_node: Node<'tree>,
6831        name: String,
6832        body: Option<Node<'tree>>,
6833        definition_body_present: bool,
6834        explicit_range: Option<Range>,
6835        raw_supertypes: Option<Vec<String>>,
6836        base_facts: Option<Vec<CppBaseSpecifierFact>>,
6837        scope: &ScopeInfo,
6838        stack: &mut Vec<CppWork<'tree>>,
6839        ancestry: &ParentIndex<'tree>,
6840    ) -> CodeUnit {
6841        let displaced_macro_tail = if explicit_range.is_none() {
6842            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
6843        } else {
6844            None
6845        };
6846        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
6847        let recovered_scope = self.scope_for_recovered_exported_class(
6848            declaration_node,
6849            &name,
6850            definition_body_present,
6851            scope,
6852            ancestry,
6853        );
6854        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
6855        // aggregate's member list is declared at the enclosing non-aggregate
6856        // scope, not nested inside the aggregate. `scope.class_unit` is the
6857        // only aggregate carrier in this walk, so dropping it puts the tag at
6858        // the nearest enclosing non-aggregate scope -- the module at file or
6859        // namespace scope, and the same block-scope representation a
6860        // function-local aggregate already gets. The tag's own body scope
6861        // below still owns its members, so fields and enumerators are
6862        // unaffected.
6863        let c_tag_scope;
6864        let c_tag_scope_applies =
6865            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
6866        if !self.c_tag_semantics && c_tag_scope_applies {
6867            self.c_tag_scope_witness = true;
6868        }
6869        let scope = if self.c_tag_semantics && c_tag_scope_applies {
6870            c_tag_scope = ScopeInfo {
6871                class_unit: None,
6872                ..recovered_scope.clone()
6873            };
6874            &c_tag_scope
6875        } else {
6876            &recovered_scope
6877        };
6878        let short_name = if let Some(parent) = &scope.class_unit {
6879            cpp_join_nested_short(parent.short_name(), &name)
6880        } else {
6881            name.clone()
6882        };
6883        // A top-level out-of-line qualified class definition (`struct
6884        // Outer::Inner { ... }` inside its namespace, #2246) carries its
6885        // nesting chain as the `$`-joined display name; push one Type/Nested
6886        // segment per class so segment-pop owner navigation keeps working.
6887        // Every other leaf name stays opaque so a literal `$` in a source
6888        // identifier never crosses the split/join boundary (#2140).
6889        let qualified_chain = if scope.class_unit.is_none() {
6890            qualified_class_name_chain(declaration_node, self.source, scope)
6891                .filter(|chain| chain.join("$") == name)
6892        } else {
6893            None
6894        };
6895        let fq = if let Some(chain) = qualified_chain {
6896            let mut fq = FqName::new();
6897            cpp_push_package(&mut fq, &scope.package_name);
6898            let mut first = true;
6899            for component in chain {
6900                let kind = if first {
6901                    SegmentKind::Type
6902                } else {
6903                    SegmentKind::Nested
6904                };
6905                fq.push(cpp_segment(&component, kind));
6906                first = false;
6907            }
6908            fq
6909        } else {
6910            cpp_leaf_fq(
6911                &scope.package_name,
6912                scope.class_unit.as_ref(),
6913                &name,
6914                SegmentKind::Nested,
6915                SegmentKind::Type,
6916            )
6917        };
6918        let code_unit = CodeUnit::with_signature_and_fq(
6919            self.file.clone(),
6920            CodeUnitType::Class,
6921            scope.package_name.clone(),
6922            short_name,
6923            scope.template_signature.clone(),
6924            false,
6925            fq,
6926        );
6927        let has_body = definition_body_present;
6928        if !has_body && self.parsed.contains_declaration(&code_unit) {
6929            if let Some(range) = explicit_range {
6930                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
6931            } else {
6932                self.link_source_declaration(ancestry, &code_unit, declaration_node);
6933            }
6934            self.parsed.record_navigation_range(
6935                code_unit.clone(),
6936                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
6937            );
6938            return code_unit;
6939        }
6940        if has_body {
6941            // A forward declaration above this definition is one of the
6942            // class's declaration sites: C++ injects the class name at the
6943            // forward declaration, so a reference between the two spells this
6944            // class (#3297). Moving the unit to its body must not drop the
6945            // earlier site, exactly as a function prototype and its body stay
6946            // two ranges of one declaration (#1650).
6947            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
6948            if let Some(range) = explicit_range {
6949                self.replace_declaration_with_range_deferred(
6950                    ancestry,
6951                    code_unit.clone(),
6952                    range,
6953                    Some(declaration_node),
6954                    None,
6955                    None,
6956                );
6957            } else {
6958                self.replace_declaration_deferred(
6959                    ancestry,
6960                    code_unit.clone(),
6961                    declaration_node,
6962                    None,
6963                    None,
6964                );
6965            }
6966            for range in earlier_ranges {
6967                self.parsed.add_declaration_range(&code_unit, range);
6968            }
6969        } else {
6970            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
6971        }
6972        if let Some(declaration) = self
6973            .source_links
6974            .get(&code_unit)
6975            .and_then(|links| links.last())
6976            .copied()
6977        {
6978            let mut properties = self.source_properties.borrow_mut();
6979            let fact = properties
6980                .get_mut(&declaration)
6981                .expect("linked class has source properties");
6982            fact.occurrence_role = if has_body {
6983                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
6984            } else {
6985                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
6986            };
6987            fact.class_strength = if has_body {
6988                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
6989            } else {
6990                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
6991            };
6992            if let Some(base_facts) = base_facts.as_ref() {
6993                fact.bases = base_facts.clone();
6994            }
6995        }
6996        if let Some(raw_supertypes) = raw_supertypes {
6997            self.parsed
6998                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
6999        }
7000        self.parsed.add_signature(
7001            code_unit.clone(),
7002            render_cpp_type_signature(
7003                declaration_node,
7004                self.source,
7005                scope.template_signature.as_deref(),
7006            ),
7007        );
7008        if let Some(metadata) = &scope.template_metadata {
7009            let primary_short_name = if let Some(parent) = &scope.class_unit {
7010                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
7011            } else {
7012                metadata.primary_name.clone()
7013            };
7014            let primary_fq_name = CodeUnit::new(
7015                self.file.clone(),
7016                CodeUnitType::Class,
7017                scope.package_name.clone(),
7018                primary_short_name,
7019            )
7020            .fq_name();
7021            let mut metadata = metadata.clone();
7022            metadata.primary_fq_name = primary_fq_name;
7023            self.parsed
7024                .set_cpp_template_metadata(code_unit.clone(), metadata);
7025        }
7026        if let Some(parent) = &scope.class_unit {
7027            self.parsed.add_child(parent.clone(), code_unit.clone());
7028        } else if let Some(module) = &scope.module {
7029            self.parsed.add_child(module.clone(), code_unit.clone());
7030        }
7031
7032        if let Some(body) = body {
7033            let mut nested_scope = scope.clone();
7034            nested_scope.class_unit = Some(code_unit.clone());
7035            nested_scope.template_signature = scope.template_signature.clone();
7036            // Template metadata describes the class just created. It must not
7037            // leak into ordinary nested declarations in that class's body.
7038            // Recovered export-macro specializations carry a separate scope bit
7039            // for their declaration-shaped body members.
7040            nested_scope.template_metadata = None;
7041            // Export-macro class bodies recovered from a function_definition use
7042            // compound_statement children, whose direct fields are declarations.
7043            nested_scope.recovered_specialization_member_scope =
7044                scope.template_metadata.as_ref().is_some_and(|metadata| {
7045                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
7046                });
7047            nested_scope.declarations_are_fields =
7048                is_recovered_exported_class_container(declaration_node, self.source)
7049                    || nested_scope.recovered_specialization_member_scope;
7050            if let Some(displaced) = displaced_macro_tail {
7051                // A macro-shaped field without a source semicolon can make
7052                // tree-sitter consume the real class terminator as an ERROR
7053                // inside that field, then retain following namespace items as
7054                // later field-list children. Drain the proven class prefix
7055                // first and re-own only the structured tail with the outer
7056                // scope. The tail is pushed first because the work stack is
7057                // LIFO.
7058                push_cpp_sibling_range(
7059                    body,
7060                    displaced.split_index,
7061                    usize::MAX,
7062                    scope.clone(),
7063                    stack,
7064                );
7065                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
7066            } else {
7067                stack.push(CppWork::Container(CppContainer {
7068                    node: body,
7069                    scope: nested_scope,
7070                }));
7071            }
7072        }
7073        if declaration_node.kind() == "enum_specifier" {
7074            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
7075            if !self.has_enum_enumerator_units(&code_unit) {
7076                self.visit_enum_enumerators_from_text(
7077                    ancestry,
7078                    declaration_node,
7079                    scope,
7080                    &code_unit,
7081                );
7082            }
7083        }
7084        code_unit
7085    }
7086
7087    /// Whether the parse product already holds enumerator fields for `parent`,
7088    /// answered from the walk's carried-forward field ownership index.
7089    ///
7090    /// Built on the first enum's question and advanced by every declaration
7091    /// recorded after it, so a file that declares no enum -- most files -- pays
7092    /// nothing, and one that declares thousands pays a single pass instead of
7093    /// one per enum (#2786).
7094    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
7095        if self.field_owners.is_none() {
7096            self.field_owners = Some(CppFieldOwnerIndex::of(
7097                self.parsed.declarations().iter(),
7098                self.file,
7099            ));
7100        }
7101        debug_assert_eq!(
7102            parent.source(),
7103            self.file,
7104            "the walk's declarations are declarations of the file it is walking"
7105        );
7106        let carried = self
7107            .field_owners
7108            .as_ref()
7109            .expect("the index was just ensured")
7110            .owns_fields(parent.package_name(), parent.short_name());
7111
7112        #[cfg(debug_assertions)]
7113        assert_eq!(
7114            carried,
7115            cpp_declarations_hold_owned_fields(
7116                self.parsed.declarations(),
7117                self.file,
7118                parent.package_name(),
7119                parent.short_name()
7120            ),
7121            "the carried-forward field index must answer what a fresh declaration scan \
7122             answers for {}",
7123            parent.fq_name()
7124        );
7125
7126        carried
7127    }
7128
7129    fn visit_enum_enumerators(
7130        &mut self,
7131        ancestry: &ParentIndex<'_>,
7132        node: Node<'_>,
7133        scope: &ScopeInfo,
7134        parent: &CodeUnit,
7135    ) {
7136        walk_named_tree_preorder(node, false, |child| {
7137            if child.kind() != "enumerator" {
7138                return WalkControl::Continue;
7139            }
7140            let Some(name_node) = child.child_by_field_name("name") else {
7141                return WalkControl::Continue;
7142            };
7143            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
7144            if name.is_empty() {
7145                return WalkControl::Continue;
7146            }
7147            let code_unit = CodeUnit::new_fq(
7148                self.file.clone(),
7149                CodeUnitType::Field,
7150                scope.package_name.clone(),
7151                cpp_join_member_short(parent.short_name(), &name),
7152                parent
7153                    .fq()
7154                    .clone()
7155                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
7156            );
7157            if self.parsed.contains_declaration(&code_unit) {
7158                return WalkControl::Continue;
7159            }
7160            self.add_declaration(
7161                ancestry,
7162                code_unit.clone(),
7163                child,
7164                Some(parent.clone()),
7165                None,
7166            );
7167            self.parsed.add_signature(
7168                code_unit,
7169                normalize_cpp_whitespace(node_text(child, self.source)),
7170            );
7171            WalkControl::Continue
7172        });
7173    }
7174
7175    fn visit_enum_enumerators_from_text(
7176        &mut self,
7177        ancestry: &ParentIndex<'_>,
7178        node: Node<'_>,
7179        scope: &ScopeInfo,
7180        parent: &CodeUnit,
7181    ) {
7182        let text = node_text(node, self.source);
7183        let Some((_, body)) = text.split_once('{') else {
7184            return;
7185        };
7186        let Some((body, _)) = body.rsplit_once('}') else {
7187            return;
7188        };
7189        for entry in body.split(',') {
7190            let trimmed = entry.trim();
7191            let name = trimmed
7192                .split('=')
7193                .next()
7194                .unwrap_or("")
7195                .split_whitespace()
7196                .next()
7197                .unwrap_or("");
7198            if name.is_empty() {
7199                continue;
7200            }
7201            let code_unit = CodeUnit::new_fq(
7202                self.file.clone(),
7203                CodeUnitType::Field,
7204                scope.package_name.clone(),
7205                cpp_join_member_short(parent.short_name(), name),
7206                parent
7207                    .fq()
7208                    .clone()
7209                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
7210            );
7211            if self.parsed.contains_declaration(&code_unit) {
7212                continue;
7213            }
7214            self.add_declaration(
7215                ancestry,
7216                code_unit.clone(),
7217                node,
7218                Some(parent.clone()),
7219                None,
7220            );
7221            self.parsed.add_signature(code_unit, trimmed.to_string());
7222        }
7223    }
7224
7225    fn visit_function_definition<'tree>(
7226        &mut self,
7227        node: Node<'tree>,
7228        scope: &ScopeInfo,
7229        stack: &mut Vec<CppWork<'tree>>,
7230        ancestry: &ParentIndex<'tree>,
7231    ) {
7232        // An attribute-like macro invocation whose argument is a declaration can
7233        // swallow every declaration written after it into one bogus
7234        // `function_definition` (#2551). This owns the whole region, so it runs
7235        // ahead of `visit_macro_swallowed_function_declarations` below, which
7236        // admits the same envelope by its head macro token and reads single
7237        // declarators out of nodes this recovery reparses properly.
7238        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
7239            return;
7240        }
7241        // A file-scope object-like macro sentinel the parser cannot see (issue
7242        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
7243        // prefixes as a bogus `function_definition` that swallows real namespaces,
7244        // classes, and members. Reparse the swallowed interior as C++ items so the
7245        // ordinary declaration visitors index it with byte/line-exact ownership.
7246        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
7247            return;
7248        }
7249        if node.has_error() {
7250            self.visit_macro_swallowed_function_declarations(node, scope);
7251        }
7252        if let Some((class_node, name, raw_supertypes, base_facts)) =
7253            recover_exported_class_function_definition(node, self.source)
7254        {
7255            if let Some(body) = cpp_body_node(node)
7256                && let Some(close) = self
7257                    .orphaned_namespaces
7258                    .matching_close_brace(body.start_byte())
7259                && close.end_byte < body.end_byte()
7260            {
7261                let class_range = Range {
7262                    start_byte: node.start_byte(),
7263                    end_byte: close.end_byte,
7264                    start_line: node.start_position().row + 1,
7265                    end_line: close.end_line,
7266                };
7267                // Parse the body in class context, preserving the original
7268                // keyword, recovered name and body as included ranges. Parsing
7269                // the interior at file scope turns constructors and access
7270                // labels into another oversized ERROR instead of members.
7271                let mut head = vec![node];
7272                let mut keyword = None;
7273                let mut name_node = None;
7274                while let Some(part) = head.pop() {
7275                    if part.start_byte() >= body.start_byte() || part.is_missing() {
7276                        continue;
7277                    }
7278                    if matches!(part.kind(), "class" | "struct" | "union") {
7279                        keyword = Some(part);
7280                    }
7281                    if matches!(part.kind(), "identifier" | "type_identifier")
7282                        && node_text(part, self.source) == name
7283                    {
7284                        name_node = Some(part);
7285                    }
7286                    let mut cursor = part.walk();
7287                    head.extend(part.children(&mut cursor));
7288                }
7289                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
7290                    && let Some(type_name) = keyword
7291                        .parent()
7292                        .and_then(|parent| parent.child_by_field_name("name"))
7293                    && let Some(tree) = parse_source_ranges_with_cancellation(
7294                        &tree_sitter_cpp::LANGUAGE.into(),
7295                        self.source,
7296                        &[
7297                            (keyword.start_byte(), type_name.start_byte()),
7298                            (name_node.start_byte(), name_node.end_byte()),
7299                            (body.start_byte(), close.end_byte),
7300                        ],
7301                        None,
7302                    )
7303                    && let Some(reparsed_class) = tree.root_node().named_child(0)
7304                    && let Some(class_body) = cpp_body_node(reparsed_class)
7305                    && class_body.start_byte() == body.start_byte()
7306                    && class_body.end_byte() == close.end_byte
7307                    && let Some(tail) =
7308                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
7309                {
7310                    let class_body_range = class_body.byte_range();
7311                    self.with_recovery_source_context(node, |visitor| {
7312                        let source_context = visitor.active_source_context.clone();
7313                        let class_unit = visitor.visit_named_class_like_shape(
7314                            class_node,
7315                            name,
7316                            None,
7317                            true,
7318                            Some(class_range),
7319                            raw_supertypes,
7320                            base_facts,
7321                            scope,
7322                            stack,
7323                            ancestry,
7324                        );
7325                        visitor.parsed.record_materialization(
7326                            MaterializationRecord::RecoveredDeclaration {
7327                                recovery: class_range,
7328                                unit: class_unit.clone(),
7329                            },
7330                        );
7331                        let member_scope = ScopeInfo {
7332                            package_name: class_unit.package_name().to_string(),
7333                            class_unit: Some(class_unit),
7334                            declarations_are_fields: true,
7335                            template_metadata: None,
7336                            recovered_specialization_member_scope: false,
7337                            ..scope.clone()
7338                        };
7339                        let tail_range = tail.root_node().byte_range();
7340                        let class_body_recovery = cpp_recovery_window(
7341                            visitor.source,
7342                            class_body_range.start,
7343                            class_body_range.end,
7344                        );
7345                        let tail_recovery =
7346                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
7347                        visitor.partitioned_regions.push(CppPartitionedRegion {
7348                            tree: tail,
7349                            range: tail_range,
7350                            scope: scope.clone(),
7351                            recovery: tail_recovery,
7352                            source_context: source_context.clone(),
7353                        });
7354                        visitor.partitioned_regions.push(CppPartitionedRegion {
7355                            tree,
7356                            range: class_body_range,
7357                            scope: member_scope,
7358                            recovery: class_body_recovery,
7359                            source_context: source_context.clone(),
7360                        });
7361                    });
7362                    return;
7363                }
7364            }
7365            let body = cpp_body_node(class_node);
7366            let displaced_namespace = cpp_body_node(node)
7367                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
7368            let fragmented = cpp_body_node(node).and_then(|body| {
7369                fragmented_export_function_body_region(
7370                    node,
7371                    body,
7372                    self.source,
7373                    displaced_namespace.as_ref(),
7374                )
7375            });
7376            // The recovery tuple's first node is the class-like type when the
7377            // parser exposes one, but the synthetic wrapper owns the compound
7378            // statement that contains the truncated class body. Use the
7379            // wrapper body for fragmented-member detection; retain the
7380            // class-node body for the ordinary (non-fragmented) path below.
7381            if let Some(fragmented) = fragmented {
7382                // The lifted sibling no longer sits below the parser-visible
7383                // namespace node. Restore the current parent scope when the
7384                // ordinary work walk reaches that class.
7385                if let Some(boundary) =
7386                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
7387                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
7388                {
7389                    let mut boundary_scope = scope.clone();
7390                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
7391                        if sibling.start_byte() >= boundary.start_byte() {
7392                            break;
7393                        }
7394                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
7395                            boundary_scope.visible_using_namespaces.push(namespace);
7396                        }
7397                    }
7398                    self.recovered_class_sibling_scopes
7399                        .insert(boundary.id(), boundary_scope);
7400                }
7401                let mut recovered_constructor = None;
7402                let mut recovered_prefix_tree = None;
7403                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
7404                {
7405                    Some(FragmentedExportMembers::Complete(tree)) => {
7406                        if let Some(body) = body
7407                            && let Some(range) =
7408                                cpp_reparsed_synthetic_initializer_constructor_range(
7409                                    tree.root_node(),
7410                                    &name,
7411                                    self.source,
7412                                    body.end_byte(),
7413                                )
7414                        {
7415                            recovered_constructor = Some(range);
7416                            recovered_prefix_tree = Some(tree);
7417                            None
7418                        } else {
7419                            Some(FragmentedExportMembers::Complete(tree))
7420                        }
7421                    }
7422                    outcome => outcome,
7423                };
7424                let mut class_stack = Vec::new();
7425                let class_unit = self.visit_named_class_like_shape(
7426                    class_node,
7427                    name,
7428                    None,
7429                    true,
7430                    Some(fragmented.class_range),
7431                    raw_supertypes,
7432                    base_facts.clone(),
7433                    scope,
7434                    &mut class_stack,
7435                    ancestry,
7436                );
7437                self.parsed
7438                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
7439                        recovery: fragmented.class_range,
7440                        unit: class_unit.clone(),
7441                    });
7442                let complete = outcome.is_some_and(|outcome| {
7443                    self.visit_fragmented_export_class_members(
7444                        node,
7445                        outcome,
7446                        class_unit.clone(),
7447                        scope,
7448                    )
7449                });
7450                if complete {
7451                    self.consumed_fragment_regions
7452                        .push((node.start_byte(), fragmented.class_range.end_byte));
7453                } else {
7454                    // The reparse can fail when the first constructor or a
7455                    // method body is split into statement-shaped siblings.
7456                    // Keep the recovered class envelope, but do not visit the
7457                    // synthetic wrapper body: its initializer expressions can
7458                    // look like same-named member functions (for example
7459                    // `Token.location(loc)`). Re-own only the original sibling
7460                    // nodes that fall inside the proven class range. Their CST
7461                    // shapes retain the real field/function kinds and ranges.
7462                    let member_scope = ScopeInfo {
7463                        package_name: class_unit.package_name().to_string(),
7464                        module: scope.module.clone(),
7465                        class_unit: Some(class_unit.clone()),
7466                        template_signature: scope.template_signature.clone(),
7467                        template_metadata: None,
7468                        declarations_are_fields: true,
7469                        recovered_specialization_member_scope: false,
7470                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
7471                    };
7472                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
7473                        if candidate.start_byte() >= fragmented.reparse_end {
7474                            break;
7475                        }
7476                        if cpp_fragment_sibling_is_class_member(
7477                            candidate,
7478                            fragmented.reparse_end,
7479                            self.source,
7480                        ) {
7481                            self.recovered_class_sibling_scopes
7482                                .insert(candidate.id(), member_scope.clone());
7483                        }
7484                    }
7485                    if let Some(range) = recovered_constructor
7486                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
7487                    {
7488                        let prefix_root = prefix_tree.root_node();
7489                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
7490                        self.visit_recovered_fragment_prefix_members(
7491                            node,
7492                            prefix_root,
7493                            range.start,
7494                            &class_unit,
7495                            scope,
7496                            &prefix_ancestry,
7497                        );
7498                        self.visit_recovered_fragment_constructor(
7499                            range,
7500                            body,
7501                            class_node,
7502                            &class_unit,
7503                            scope,
7504                            ancestry,
7505                        );
7506                    }
7507                }
7508                if let Some(boundary) = displaced_namespace {
7509                    for item in boundary.namespace_items {
7510                        self.recovered_class_sibling_scopes
7511                            .insert(item.id(), scope.clone());
7512                    }
7513                }
7514                stack.extend(class_stack);
7515                return;
7516            }
7517            let mut stack = Vec::new();
7518            let class_unit = self.visit_named_class_like_shape(
7519                class_node,
7520                name,
7521                body,
7522                body.is_some(),
7523                None,
7524                raw_supertypes,
7525                base_facts,
7526                scope,
7527                &mut stack,
7528                ancestry,
7529            );
7530            self.parsed
7531                .record_materialization(MaterializationRecord::RecoveredDeclaration {
7532                    recovery: cpp_declaration_range(node),
7533                    unit: class_unit,
7534                });
7535            // Issue #1524: the bogus `function_definition` body can run past
7536            // the class's true closing brace (the parse ends it with a
7537            // zero-width `MISSING "}"`), swallowing following namespace-scope
7538            // siblings -- they would index as members of the recovered class.
7539            // When the body's text-balanced close lands before the body's own
7540            // end, re-own the swallowed tail with the outer scope instead.
7541            if let Some(body) = body
7542                && let Some(class_close) = self
7543                    .orphaned_namespaces
7544                    .matching_close_brace(body.start_byte())
7545                && class_close.start_byte < body.end_byte()
7546            {
7547                let split = {
7548                    let mut cursor = body.walk();
7549                    body.named_children(&mut cursor)
7550                        .position(|child| child.start_byte() > class_close.start_byte)
7551                };
7552                if let Some(split) = split {
7553                    // The seeded work is a single Container over the whole
7554                    // body with the class scope; replace it with the bounded
7555                    // head (class scope) plus the swallowed tail (outer
7556                    // scope). Push tail first so the head drains first.
7557                    let seeded = stack.pop();
7558                    match seeded {
7559                        Some(CppWork::Container(container)) => {
7560                            push_cpp_sibling_range(
7561                                body,
7562                                split,
7563                                usize::MAX,
7564                                scope.clone(),
7565                                &mut stack,
7566                            );
7567                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
7568                        }
7569                        // visit_named_class_like_shape always seeds exactly
7570                        // one Container when a body is present.
7571                        _ => unreachable!("exported-class seed is always one Container"),
7572                    }
7573                }
7574            }
7575            while let Some(work) = stack.pop() {
7576                match work {
7577                    CppWork::Container(container) => {
7578                        push_cpp_container_work(container.node, container.scope, &mut stack);
7579                    }
7580                    CppWork::Siblings(siblings) => {
7581                        advance_cpp_siblings(siblings, self.source, &mut stack);
7582                    }
7583                    CppWork::Node(work) => {
7584                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
7585                    }
7586                }
7587            }
7588            return;
7589        }
7590        let recovered_constraint_constructor =
7591            cpp_recovered_template_macro_constructor(node, self.source);
7592        let declarator = recovered_constraint_constructor
7593            .map(|(declarator, _)| declarator)
7594            .or_else(|| node.child_by_field_name("declarator"));
7595        let Some(declarator) = declarator else {
7596            self.visit_malformed_function_definition_container(node, scope, stack);
7597            return;
7598        };
7599        let Some(function_declarator) = extract_function_declarator(declarator) else {
7600            self.visit_malformed_function_definition_container(node, scope, stack);
7601            return;
7602        };
7603        let function = if let Some((_, callable_name)) =
7604            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
7605        {
7606            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
7607        } else {
7608            extract_function_info(function_declarator, self.source, scope)
7609        };
7610        let Some(mut function) = function else {
7611            self.visit_malformed_function_definition_container(node, scope, stack);
7612            return;
7613        };
7614        if let Some((_, template_parameter)) = recovered_constraint_constructor {
7615            function.signature = format!(
7616                "template <{}>{}",
7617                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
7618                function.signature
7619            );
7620        }
7621        let code_unit = function.code_unit(self.file.clone());
7622        // Keep an earlier same-file prototype as another physical occurrence
7623        // of this callable. `CodeUnit` already identifies the role-neutral
7624        // overload, while ranges and signature metadata describe its
7625        // declaration/definition occurrences.
7626        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
7627        let signature = if recovered_constraint_constructor.is_some() {
7628            normalize_cpp_whitespace(node_text(function_declarator, self.source))
7629        } else {
7630            render_cpp_function_display_signature_from_node(
7631                node,
7632                self.source,
7633                scope.template_signature.as_deref(),
7634                true,
7635                ancestry,
7636            )
7637        };
7638        self.add_signature_with_metadata(
7639            code_unit.clone(),
7640            cpp_signature_metadata(
7641                signature,
7642                function_declarator,
7643                cpp_callable_linkage(node, self.source, ancestry),
7644                self.source,
7645                ancestry,
7646            )
7647            .with_declaration_only(false),
7648        );
7649        if let Some(parent) = &scope.class_unit {
7650            self.parsed.add_child(parent.clone(), code_unit);
7651        } else if let Some(module) = &scope.module {
7652            self.parsed.add_child(module.clone(), code_unit);
7653        }
7654        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
7655    }
7656
7657    /// Recover the namespace lost when tree-sitter promotes an export-macro
7658    /// class definition to a root-level `function_definition`.  Only a
7659    /// body-bearing, top-level recovery may borrow a namespace, and only when
7660    /// one earlier namespace-scope forward declaration proves the identity.
7661    fn scope_for_recovered_exported_class<'tree>(
7662        &mut self,
7663        node: Node<'tree>,
7664        name: &str,
7665        definition_body_present: bool,
7666        scope: &ScopeInfo,
7667        ancestry: &ParentIndex<'tree>,
7668    ) -> ScopeInfo {
7669        if !definition_body_present
7670            || !scope.package_name.is_empty()
7671            || scope.class_unit.is_some()
7672            || !(is_recovered_exported_class_container(node, self.source)
7673                || recover_function_like_export_class_pair(node, self.source).is_some()
7674                || recover_embedded_function_like_export_classes(node, self.source)
7675                    .iter()
7676                    .any(|recovered| recovered.name == name)
7677                || matches!(node.kind(), "declaration" | "field_declaration")
7678                    && recover_exported_class_declaration(node, self.source).is_some()
7679                || matches!(
7680                    node.kind(),
7681                    "class_specifier" | "struct_specifier" | "union_specifier"
7682                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
7683                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
7684                        name_node,
7685                        self.source,
7686                    )))
7687                }) || ancestry.parent(node).is_some_and(|parent| {
7688                    matches!(parent.kind(), "declaration" | "field_declaration")
7689                        && recover_exported_class_declaration(parent, self.source).is_some()
7690                        || is_recovered_exported_class_container(parent, self.source)
7691                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
7692        {
7693            return scope.clone();
7694        }
7695        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
7696        let Some(package_name) = borrowed_namespace
7697            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
7698        else {
7699            return scope.clone();
7700        };
7701
7702        let module = CodeUnit::new_fq(
7703            self.file.clone(),
7704            CodeUnitType::Module,
7705            "",
7706            package_name.clone(),
7707            cpp_namespace_fq(&package_name),
7708        );
7709        let mut recovered = scope.clone();
7710        recovered.package_name = package_name;
7711        recovered.module = Some(module);
7712        recovered
7713    }
7714
7715    /// The unique namespace-scope forward declaration of `name` that precedes
7716    /// `recovered_node`, answered from the walk's carried-forward scan of the
7717    /// tree `recovered_node` belongs to.
7718    ///
7719    /// The scan is built on the first question and advanced by each later one,
7720    /// so a file that never reaches this path -- almost every file -- pays
7721    /// nothing, and one that reaches it thousands of times pays a single pass
7722    /// (#2754).
7723    fn unique_earlier_namespace_forward<'tree>(
7724        &mut self,
7725        recovered_node: Node<'tree>,
7726        name: &str,
7727        ancestry: &ParentIndex<'tree>,
7728    ) -> Option<String> {
7729        let mut root = recovered_node;
7730        while let Some(parent) = ancestry.parent(root) {
7731            root = parent;
7732        }
7733        let source = self.source;
7734        let scan = self
7735            .namespace_forward_scans
7736            .entry(CppTreeIdentity::of(root))
7737            .or_default();
7738        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
7739        let borrowed = scan.unique_earlier_forward(name, recovered_node);
7740
7741        #[cfg(debug_assertions)]
7742        assert_eq!(
7743            borrowed,
7744            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
7745            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
7746             for {name} at byte {}",
7747            recovered_node.start_byte()
7748        );
7749
7750        borrowed
7751    }
7752
7753    fn visit_malformed_function_definition_container<'tree>(
7754        &mut self,
7755        node: Node<'tree>,
7756        scope: &ScopeInfo,
7757        stack: &mut Vec<CppWork<'tree>>,
7758    ) {
7759        let Some(body) = cpp_body_node(node) else {
7760            return;
7761        };
7762        if !cpp_contains_namespace_definition(body) {
7763            return;
7764        }
7765        stack.push(CppWork::Container(CppContainer {
7766            node: body,
7767            scope: scope.clone(),
7768        }));
7769    }
7770
7771    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
7772    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
7773    /// emits for a sentinel-prefixed region, reparse the interior after the
7774    /// sentinel identifier as real C++ items -- confined to the region so
7775    /// every reparsed node keeps its original byte/line position -- and run the
7776    /// ordinary container visitation over the result. Returns `true` when it fired
7777    /// (the caller must then skip normal function processing). Nested sentinel
7778    /// regions recover recursively: the reparsed interior is walked through the
7779    /// same `visit_function_definition` path, so a sentinel inside the region hits
7780    /// this recovery again.
7781    /// Runs `reparse_walk` and records every declaration it mints as a
7782    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
7783    /// `recovery` (issue #1657). A reparsed sentinel region has no single
7784    /// recovered envelope unit: the ordinary visitors mint namespaces,
7785    /// classes, and members directly from the reparsed tree, so the walk's
7786    /// declaration delta is the recovered set. Records are ordered by
7787    /// declaration start byte so the parse product stays deterministic.
7788    fn record_recovered_declarations(
7789        &mut self,
7790        recovery: Range,
7791        origin: Node<'_>,
7792        reparse_walk: impl FnOnce(&mut Self),
7793    ) {
7794        self.with_recovery_source_context(origin, |visitor| {
7795            let source_context = visitor.active_source_context.clone();
7796            visitor.record_recovered_declarations_with_context(
7797                recovery,
7798                source_context,
7799                reparse_walk,
7800            );
7801        });
7802    }
7803
7804    /// Run a recovered walk with an owned source context and record every
7805    /// declaration it mints. Deferred partitioned regions cannot retain their
7806    /// origin `Node`, so they capture the context while the origin tree is
7807    /// live and use this same materialization path when drained.
7808    fn record_recovered_declarations_with_context(
7809        &mut self,
7810        recovery: Range,
7811        source_context: Option<CppActiveSourceContext>,
7812        reparse_walk: impl FnOnce(&mut Self),
7813    ) {
7814        // The set difference this used to be, kept as the oracle every answer
7815        // is asserted against (#2787).
7816        #[cfg(any(debug_assertions, test))]
7817        let before = self.parsed.declarations().clone();
7818
7819        self.recovery_captures.push(CppRecoveryCapture::default());
7820        let previous = self.active_source_context.take();
7821        self.active_source_context = source_context;
7822        reparse_walk(self);
7823        self.active_source_context = previous;
7824        let captured = self
7825            .recovery_captures
7826            .pop()
7827            .expect("the capture this call pushed is the one it pops");
7828
7829        // The capture holds every declaration created while it was open, once
7830        // each and in creation order, so the recovered set costs what the
7831        // recovery made rather than everything the file has declared so far.
7832        // One filter is left to apply: a created declaration that a later
7833        // deferred replacement removed is not in the parse product to report.
7834        let mut minted: Vec<CodeUnit> = captured
7835            .created
7836            .into_iter()
7837            .filter(|unit| self.parsed.contains_declaration(unit))
7838            .collect();
7839        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
7840
7841        #[cfg(any(debug_assertions, test))]
7842        {
7843            let mut rediscovered: Vec<CodeUnit> = self
7844                .parsed
7845                .declarations()
7846                .iter()
7847                .filter(|unit| !before.contains(*unit))
7848                .cloned()
7849                .collect();
7850            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
7851            assert_eq!(
7852                minted, rediscovered,
7853                "the captured recovered set must be the declaration delta of the reparse \
7854                 walk over {recovery:?}"
7855            );
7856        }
7857
7858        for unit in minted {
7859            self.parsed
7860                .record_materialization(MaterializationRecord::RecoveredDeclaration {
7861                    recovery,
7862                    unit,
7863                });
7864        }
7865    }
7866
7867    /// Where one recovered declaration sorts: by start byte, then by name, so
7868    /// the parse product stays deterministic.
7869    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
7870        let start = self
7871            .parsed
7872            .declaration_ranges(unit)
7873            .first()
7874            .map(|range| range.start_byte)
7875            .unwrap_or(usize::MAX);
7876        (start, unit.fq_name().to_string())
7877    }
7878
7879    fn visit_sentinel_macro_region<'tree>(
7880        &mut self,
7881        node: Node<'tree>,
7882        scope: &ScopeInfo,
7883        stack: &mut Vec<CppWork<'tree>>,
7884        ancestry: &ParentIndex<'tree>,
7885    ) -> bool {
7886        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
7887            return true;
7888        }
7889        if let Some((
7890            reparse_start,
7891            class_start,
7892            body_start,
7893            class_close_start,
7894            class_close_end,
7895            class_close_line,
7896        )) = cpp_sentinel_macro_class_region(node, self.source)
7897        {
7898            let Some(class_tree) =
7899                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
7900            else {
7901                return false;
7902            };
7903            let class_root = class_tree.root_node();
7904            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
7905            // A region reparse is its own tree and needs its own parent index.
7906            let class_ancestry = self.recovery_ancestry(class_root);
7907            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
7908                class_root,
7909                template_node,
7910                self.source,
7911                &class_ancestry,
7912            ) else {
7913                return false;
7914            };
7915            let class_node = reparsed_class.declaration_node;
7916            let name = reparsed_class.name;
7917            let mut class_scope = scope.clone();
7918            if let Some(template_node) = template_node {
7919                class_scope.template_signature =
7920                    cpp_template_signature(template_node, class_node, self.source);
7921                class_scope.template_metadata =
7922                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
7923            }
7924            let Some(body_tree) =
7925                cpp_reparse_region_items(self.source, body_start, class_close_start)
7926            else {
7927                return false;
7928            };
7929            let raw_supertypes = reparsed_class.raw_supertypes;
7930            let base_facts = reparsed_class.base_facts;
7931            let class_range = Range {
7932                start_byte: class_start,
7933                end_byte: class_close_end,
7934                start_line: class_node.start_position().row + 1,
7935                end_line: class_close_line,
7936            };
7937            let class_scope = self.scope_for_recovered_exported_class(
7938                class_node,
7939                &name,
7940                true,
7941                &class_scope,
7942                &class_ancestry,
7943            );
7944            let mut class_stack = Vec::new();
7945            self.with_recovery_source_context(node, |visitor| {
7946                let class_unit = visitor.visit_named_class_like_shape(
7947                    class_node,
7948                    name,
7949                    None,
7950                    true,
7951                    Some(class_range),
7952                    raw_supertypes,
7953                    base_facts,
7954                    &class_scope,
7955                    &mut class_stack,
7956                    &class_ancestry,
7957                );
7958                visitor.parsed.record_materialization(
7959                    MaterializationRecord::RecoveredDeclaration {
7960                        recovery: class_range,
7961                        unit: class_unit.clone(),
7962                    },
7963                );
7964                let member_scope = ScopeInfo {
7965                    package_name: class_scope.package_name.clone(),
7966                    module: class_scope.module.clone(),
7967                    class_unit: Some(class_unit),
7968                    template_signature: class_scope.template_signature.clone(),
7969                    template_metadata: None,
7970                    declarations_are_fields: true,
7971                    recovered_specialization_member_scope: false,
7972                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
7973                };
7974                // The padded body reparse is its own tree, so it indexes itself.
7975                let body_root = body_tree.root_node();
7976                visitor.run_container_work(
7977                    body_root,
7978                    member_scope,
7979                    &visitor.recovery_ancestry(body_root),
7980                );
7981            });
7982            // Register only after the padded body reparse: its nodes deliberately
7983            // retain offsets inside the consumed region and must be visited first.
7984            self.consumed_fragment_regions
7985                .push((node.start_byte(), class_close_end));
7986            // An ERROR envelope can hold real sibling declarations after the
7987            // recovered class's close (the suffix-reparse boundary in
7988            // `cpp_sentinel_macro_class_region` partitions, it does not
7989            // consume). Walk the envelope's remaining children normally; the
7990            // consumed region above keeps the recovered class from being
7991            // indexed twice.
7992            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
7993                stack.push(CppWork::Container(CppContainer {
7994                    node,
7995                    scope: scope.clone(),
7996                }));
7997            }
7998            return true;
7999        }
8000        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
8001            return false;
8002        };
8003        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
8004            return false;
8005        };
8006        let root = tree.root_node();
8007        if !cpp_reparsed_items_are_indexable(root, self.source) {
8008            return false;
8009        }
8010        let recovery = cpp_recovery_window(self.source, start, end);
8011        // The reparsed region is its own tree, so this walk indexes it itself.
8012        let reparsed_ancestry = self.recovery_ancestry(root);
8013        self.record_recovered_declarations(recovery, node, |visitor| {
8014            visitor.visit_container(
8015                root,
8016                &reparsed_ancestry,
8017                &scope.package_name,
8018                scope.module.clone(),
8019                scope.class_unit.clone(),
8020                scope.template_signature.clone(),
8021                scope.visible_using_namespaces.clone(),
8022            );
8023        });
8024        if end > node.end_byte() {
8025            self.consumed_fragment_regions
8026                .push((node.start_byte(), end));
8027        } else if node.kind() == "ERROR" && node.end_byte() > end {
8028            // The sentinel region ended at the first recovered class-like item
8029            // but the ERROR envelope keeps real sibling declarations after it
8030            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
8031            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
8032            // envelope's remaining children normally; the consumed region
8033            // keeps the reparsed prefix from being indexed twice.
8034            self.consumed_fragment_regions
8035                .push((node.start_byte(), end));
8036            stack.push(CppWork::Container(CppContainer {
8037                node,
8038                scope: scope.clone(),
8039            }));
8040        }
8041        true
8042    }
8043
8044    /// Re-own complete class declarations from the structured Abseil
8045    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
8046    /// its direct CST children already prove both namespace components and the
8047    /// class bodies, so the ordinary class/member visitor can retain ownership
8048    /// and exact source ranges without admitting unrelated callable bodies.
8049    fn visit_nested_namespace_sentinel<'tree>(
8050        &mut self,
8051        node: Node<'tree>,
8052        scope: &ScopeInfo,
8053        ancestry: &ParentIndex<'tree>,
8054    ) -> bool {
8055        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
8056            return false;
8057        };
8058
8059        let mut package_name = scope.package_name.clone();
8060        let mut module = scope.module.clone();
8061        for component in recovered.namespace_components {
8062            package_name = if package_name.is_empty() {
8063                component
8064            } else {
8065                format!("{package_name}::{component}")
8066            };
8067            let namespace_module = CodeUnit::new_fq(
8068                self.file.clone(),
8069                CodeUnitType::Module,
8070                "",
8071                package_name.clone(),
8072                cpp_namespace_fq(&package_name),
8073            );
8074            if !self.parsed.contains_declaration(&namespace_module) {
8075                self.add_declaration(
8076                    ancestry,
8077                    namespace_module.clone(),
8078                    recovered.function,
8079                    None,
8080                    None,
8081                );
8082            }
8083            module = Some(namespace_module);
8084        }
8085
8086        let recovered_scope = ScopeInfo {
8087            package_name,
8088            module,
8089            // This scope's package comes from the namespace levels the sentinel
8090            // shape proves, so its owner must come from the same place. C++
8091            // never nests a namespace inside a class, so a surviving
8092            // `class_unit` here is recovery bleed from an earlier malformed
8093            // region, and keeping it would publish declarations whose package
8094            // names one lexical position and whose owner names another -- the
8095            // hybrid identity that trips the package/short boundary assert in
8096            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
8097            // identity-neutral for valid code, where `class_unit` is always
8098            // empty at a namespace.
8099            class_unit: None,
8100            template_signature: scope.template_signature.clone(),
8101            template_metadata: scope.template_metadata.clone(),
8102            declarations_are_fields: false,
8103            recovered_specialization_member_scope: false,
8104            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8105        };
8106        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
8107            recovered.function,
8108            recovered.body,
8109            self.source,
8110            ancestry,
8111        ) {
8112            let mut class_scope = recovered_scope.clone();
8113            if let Some(template_node) = fragmented.template_node {
8114                class_scope.template_signature =
8115                    cpp_template_signature(template_node, fragmented.class_node, self.source);
8116                class_scope.template_metadata = cpp_template_metadata(
8117                    template_node,
8118                    fragmented.class_node,
8119                    self.source,
8120                    ancestry,
8121                );
8122            }
8123            if let Some(outcome) = self
8124                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
8125            {
8126                let mut class_stack = Vec::new();
8127                let class_unit = self.visit_named_class_like_shape(
8128                    fragmented.class_node,
8129                    fragmented.name.clone(),
8130                    None,
8131                    true,
8132                    Some(fragmented.fragmented.class_range),
8133                    fragmented.raw_supertypes.clone(),
8134                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
8135                    &class_scope,
8136                    &mut class_stack,
8137                    ancestry,
8138                );
8139                self.parsed
8140                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8141                        recovery: fragmented.fragmented.class_range,
8142                        unit: class_unit.clone(),
8143                    });
8144                if self.visit_fragmented_export_class_members(
8145                    node,
8146                    outcome,
8147                    class_unit,
8148                    &class_scope,
8149                ) {
8150                    self.consumed_fragment_regions.push((
8151                        fragmented.consumed_start,
8152                        fragmented.fragmented.class_range.end_byte,
8153                    ));
8154                }
8155            }
8156        }
8157        // The class requirement above is the admission gate; once admitted,
8158        // traverse the whole proven inner namespace body so sibling aliases,
8159        // functions, and variables are not silently discarded. The body is a
8160        // node of the tree being walked, so it reuses that tree's index.
8161        self.run_container_work(recovered.body, recovered_scope, ancestry);
8162        true
8163    }
8164
8165    fn visit_declaration<'tree>(
8166        &mut self,
8167        node: Node<'tree>,
8168        scope: &ScopeInfo,
8169        in_class_body: bool,
8170        stack: &mut Vec<CppWork<'tree>>,
8171        ancestry: &ParentIndex<'tree>,
8172    ) {
8173        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8174            return;
8175        }
8176        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
8177            return;
8178        }
8179        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
8180            !cpp_active_template_type_parameter(
8181                node,
8182                node_text(declarator, self.source),
8183                self.source,
8184                ancestry,
8185            )
8186        }) {
8187            return;
8188        }
8189        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
8190            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
8191            // the following `Imaging image` member into one malformed field.
8192            // The ERROR's identifier is the actual declarator; route it
8193            // through the ordinary field path so its parent, range, and
8194            // signature metadata remain consistent with every other member.
8195            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
8196            return;
8197        }
8198        if in_class_body
8199            && let Some(parent) = scope.class_unit.as_ref()
8200            && let Some(call) =
8201                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
8202        {
8203            self.visit_recovered_macro_qualified_constructor_definition(
8204                node, call, scope, ancestry,
8205            );
8206            return;
8207        }
8208        if in_class_body
8209            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
8210        {
8211            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
8212            return;
8213        }
8214        if in_class_body
8215            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
8216        {
8217            for member in members {
8218                self.add_macro_wrapped_declaration(member, scope, ancestry);
8219            }
8220            return;
8221        }
8222        if in_class_body
8223            && let Some(declarators) =
8224                recovered_macro_qualified_field_declarators(node, self.source)
8225        {
8226            for declarator in declarators {
8227                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8228            }
8229            return;
8230        }
8231        let recovered_alias_names = recovered_type_alias_names(node, self.source);
8232        if !recovered_alias_names.is_empty() {
8233            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
8234            return;
8235        }
8236        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
8237            return;
8238        }
8239        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
8240            return;
8241        }
8242
8243        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
8244            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
8245                // Issue #938: the members tree-sitter scattered out of the fragmented
8246                // multiple-base export node are reparsed from their true body region
8247                // and re-owned as members of the recovered class, with an explicit
8248                // navigation range spanning to the displaced closing brace.
8249                if let Some(outcome) =
8250                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
8251                {
8252                    let consumed_region = (
8253                        recovered.declaration_node.end_byte(),
8254                        fragmented.class_range.end_byte,
8255                    );
8256                    let code_unit = self.visit_named_class_like_shape(
8257                        recovered.declaration_node,
8258                        recovered.name,
8259                        None,
8260                        true,
8261                        Some(fragmented.class_range),
8262                        recovered.raw_supertypes,
8263                        recovered.base_facts,
8264                        scope,
8265                        stack,
8266                        ancestry,
8267                    );
8268                    self.parsed.record_materialization(
8269                        MaterializationRecord::RecoveredDeclaration {
8270                            recovery: fragmented.class_range,
8271                            unit: code_unit.clone(),
8272                        },
8273                    );
8274                    let consume_fragment =
8275                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
8276                    // Everything between the fragmented declaration and its displaced
8277                    // closing brace now belongs to the recovered class; keep the
8278                    // ordinary walk from re-indexing those scattered siblings at top
8279                    // level. Register the consumed region only after indexing because
8280                    // the reparsed nodes retain byte offsets inside that same region.
8281                    if consume_fragment {
8282                        self.consumed_fragment_regions.push(consumed_region);
8283                    }
8284                    return;
8285                }
8286            }
8287            let uses_initializer_body = recovered.uses_initializer_body;
8288            let definition_body_present = recovered.body.is_some();
8289            let class_unit = self.visit_named_class_like_shape(
8290                recovered.declaration_node,
8291                recovered.name,
8292                recovered.body,
8293                definition_body_present,
8294                None,
8295                recovered.raw_supertypes,
8296                recovered.base_facts,
8297                scope,
8298                stack,
8299                ancestry,
8300            );
8301            self.parsed
8302                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8303                    recovery: cpp_declaration_range(node),
8304                    unit: class_unit,
8305                });
8306            if uses_initializer_body {
8307                return;
8308            }
8309        }
8310
8311        // A function-like macro invocation that expands to declarations keeps
8312        // no terminator of its own: the semicolons live in the replacement
8313        // body, which is not part of this tree. Tree-sitter recovers one and
8314        // reads the macro name as the member type and its argument list as a
8315        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
8316        // published a field named `ClosedDetect` beside the real constructor
8317        // and split every reference to the constructor's name between the two
8318        // (#3298). The invented terminator is the whole difference from a real
8319        // member: a member terminates itself, and with a terminator the
8320        // grammar reads `T (name);` as a declaration rather than a field.
8321        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
8322            return;
8323        }
8324
8325        let mut handled_function = false;
8326        let mut handled_declarator = false;
8327        let mut cursor = node.walk();
8328        for child in node.named_children(&mut cursor) {
8329            if matches!(
8330                child.kind(),
8331                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8332            ) {
8333                // A named class-like definition remains a declaration even when
8334                // the same statement also declares an object, for example
8335                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
8336                // declaration's type and `kind` as its declarator.  Dropping the
8337                // type here loses both its nested owner and every later lexical
8338                // reference to it.  A body is the structured proof that this is
8339                // a definition rather than an elaborated type use such as
8340                // `class Kind value;`.
8341                if cpp_body_node(child).is_some() {
8342                    self.visit_class_like(child, scope, stack, ancestry);
8343                }
8344                continue;
8345            }
8346        }
8347
8348        let mut cursor = node.walk();
8349        for child in node.children_by_field_name("declarator", &mut cursor) {
8350            if crate::structural::is_recovered_designator_init_declarator(child) {
8351                handled_declarator = true;
8352                continue;
8353            }
8354            if in_class_body
8355                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
8356            {
8357                handled_declarator = true;
8358                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
8359                continue;
8360            }
8361            if let Some(kind) = classify_declarator(child) {
8362                handled_declarator = true;
8363                match kind {
8364                    DeclaratorKind::Function(function_declarator) => {
8365                        handled_function = true;
8366                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
8367                    }
8368                    DeclaratorKind::Variable(variable_declarator) => {
8369                        self.visit_variable_declaration(
8370                            node,
8371                            variable_declarator,
8372                            scope,
8373                            in_class_body,
8374                            ancestry,
8375                        );
8376                    }
8377                }
8378            }
8379        }
8380
8381        if !handled_declarator {
8382            let mut cursor = node.walk();
8383            for child in node.named_children(&mut cursor) {
8384                if crate::structural::is_recovered_designator_init_declarator(child) {
8385                    handled_declarator = true;
8386                    continue;
8387                }
8388                if !is_unfielded_declarator_candidate(child) {
8389                    continue;
8390                }
8391                let Some(kind) = classify_declarator(child) else {
8392                    continue;
8393                };
8394                handled_declarator = true;
8395                match kind {
8396                    DeclaratorKind::Function(function_declarator) => {
8397                        handled_function = true;
8398                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
8399                    }
8400                    DeclaratorKind::Variable(variable_declarator) => {
8401                        self.visit_variable_declaration(
8402                            node,
8403                            variable_declarator,
8404                            scope,
8405                            in_class_body,
8406                            ancestry,
8407                        );
8408                    }
8409                }
8410            }
8411        }
8412
8413        if handled_function {
8414            return;
8415        }
8416
8417        if !handled_declarator {
8418            if in_class_body {
8419                self.visit_class_members_from_declaration(node, scope, ancestry);
8420            } else {
8421                self.visit_global_variables_from_declaration(node, scope, ancestry);
8422            }
8423        }
8424    }
8425
8426    /// Preserve the member structure of an anonymous aggregate.
8427    ///
8428    /// An anonymous union with no declarator promotes its fields into the
8429    /// containing aggregate. An anonymous struct/union followed by a named
8430    /// declarator, such as `struct { T *ops; } sock`, declares both the field
8431    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
8432    /// the declarator's structured nested identity so a later `value.sock.ops`
8433    /// chain can traverse it without parsing a type spelling (#2407).
8434    ///
8435    /// An anonymous aggregate declares no tag, so there is nothing for the C
8436    /// dialect to re-scope and nothing for the C++ dialect to nest: both
8437    /// readings of this shape are the same, and both need these members
8438    /// (#3301).
8439    fn visit_anonymous_aggregate_declaration<'tree>(
8440        &mut self,
8441        node: Node<'tree>,
8442        scope: &ScopeInfo,
8443        in_class_body: bool,
8444        stack: &mut Vec<CppWork<'tree>>,
8445        ancestry: &ParentIndex<'tree>,
8446    ) -> bool {
8447        if !in_class_body || scope.class_unit.is_none() {
8448            return false;
8449        }
8450        let Some(aggregate) = node.child_by_field_name("type") else {
8451            return false;
8452        };
8453        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
8454            || aggregate.child_by_field_name("name").is_some()
8455        {
8456            return false;
8457        }
8458        let Some(body) = cpp_body_node(aggregate) else {
8459            return false;
8460        };
8461
8462        let mut cursor = node.walk();
8463        let declarators = node
8464            .children_by_field_name("declarator", &mut cursor)
8465            .filter_map(|declarator| match classify_declarator(declarator) {
8466                Some(DeclaratorKind::Variable(variable)) => Some(variable),
8467                Some(DeclaratorKind::Function(_)) | None => None,
8468            })
8469            .collect::<Vec<_>>();
8470        if declarators.is_empty() {
8471            stack.push(CppWork::Container(CppContainer {
8472                node: body,
8473                scope: scope.clone(),
8474            }));
8475            return true;
8476        }
8477
8478        for declarator in declarators {
8479            let Some(name) = extract_variable_name(declarator, self.source) else {
8480                continue;
8481            };
8482            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8483            // The receiver type's identity is the declarator's name, so its
8484            // range is the field declaration that carries that name token.
8485            // The inner specifier ends at the closing brace, which leaves the
8486            // name the unit is known by outside its own text (#3287).
8487            self.visit_named_class_like_shape(
8488                aggregate,
8489                name,
8490                Some(body),
8491                true,
8492                Some(cpp_declaration_range(node)),
8493                None,
8494                None,
8495                scope,
8496                stack,
8497                ancestry,
8498            );
8499        }
8500        true
8501    }
8502
8503    /// Give a function-local anonymous aggregate a structured owner so its
8504    /// direct pointer bindings can be typed by the usage graph.  Unlike an
8505    /// anonymous aggregate in a class body, there is no source-level tag or
8506    /// typedef to supply an identity.  The aggregate's CST range is therefore
8507    /// part of a generated, collision-resistant identity; no source spelling
8508    /// is consulted.  The declaration's own variable remains a file-level
8509    /// field projection, while the aggregate body is visited as the generated
8510    /// class's field list.
8511    fn visit_anonymous_local_aggregate_declaration<'tree>(
8512        &mut self,
8513        node: Node<'tree>,
8514        scope: &ScopeInfo,
8515        stack: &mut Vec<CppWork<'tree>>,
8516        ancestry: &ParentIndex<'tree>,
8517    ) -> bool {
8518        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
8519            return false;
8520        }
8521        let Some(aggregate) = node.child_by_field_name("type") else {
8522            return false;
8523        };
8524        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
8525            || aggregate.child_by_field_name("name").is_some()
8526        {
8527            return false;
8528        }
8529        let Some(body) = cpp_body_node(aggregate) else {
8530            return false;
8531        };
8532        let mut cursor = node.walk();
8533        let declarators = node
8534            .children_by_field_name("declarator", &mut cursor)
8535            .filter_map(|declarator| match classify_declarator(declarator) {
8536                Some(DeclaratorKind::Variable(variable)) => Some(variable),
8537                Some(DeclaratorKind::Function(_)) | None => None,
8538            })
8539            .collect::<Vec<_>>();
8540        if declarators.is_empty() {
8541            return false;
8542        }
8543
8544        for declarator in &declarators {
8545            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
8546        }
8547        let name = format!("<anonymous:{}>", aggregate.start_byte());
8548        self.visit_named_class_like_shape(
8549            aggregate,
8550            name,
8551            Some(body),
8552            true,
8553            None,
8554            None,
8555            None,
8556            scope,
8557            stack,
8558            ancestry,
8559        );
8560        true
8561    }
8562
8563    fn visit_function_declaration<'tree>(
8564        &mut self,
8565        declaration_node: Node<'tree>,
8566        declarator: Node<'tree>,
8567        scope: &ScopeInfo,
8568        ancestry: &ParentIndex<'tree>,
8569    ) {
8570        let Some(function) = extract_function_info(declarator, self.source, scope) else {
8571            return;
8572        };
8573        // An in-class member prototype is a real source declaration, not a
8574        // generated or recovery-only unit, so it keeps the ordinary code-unit
8575        // identity (#3508). #3493 recorded its callable modifiers, which is
8576        // what a canonical procedure key needs; a synthetic unit would still
8577        // refuse that key on identity grounds alone.
8578        let code_unit = function.code_unit(self.file.clone());
8579        if self.parsed.contains_declaration(&code_unit) {
8580            self.link_source_declaration(ancestry, &code_unit, declaration_node);
8581            self.parsed
8582                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
8583            return;
8584        }
8585        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8586        let signature = render_cpp_function_display_signature_from_node(
8587            declaration_node,
8588            self.source,
8589            scope.template_signature.as_deref(),
8590            false,
8591            ancestry,
8592        );
8593        self.add_signature_with_metadata(
8594            code_unit.clone(),
8595            cpp_signature_metadata(
8596                signature,
8597                declarator,
8598                cpp_callable_linkage(declaration_node, self.source, ancestry),
8599                self.source,
8600                ancestry,
8601            )
8602            .with_declaration_only(true),
8603        );
8604        if let Some(parent) = &scope.class_unit {
8605            self.parsed.add_child(parent.clone(), code_unit);
8606        } else if let Some(module) = &scope.module {
8607            self.parsed.add_child(module.clone(), code_unit);
8608        }
8609    }
8610
8611    fn visit_recovered_macro_qualified_function_declaration<'tree>(
8612        &mut self,
8613        declaration_node: Node<'tree>,
8614        call: Node<'tree>,
8615        scope: &ScopeInfo,
8616        ancestry: &ParentIndex<'tree>,
8617    ) {
8618        let Some(parent) = &scope.class_unit else {
8619            return;
8620        };
8621        let Some(name_node) = call.child_by_field_name("function") else {
8622            return;
8623        };
8624        let Some(arguments) = call.child_by_field_name("arguments") else {
8625            return;
8626        };
8627        let Some((signature, parameter_labels)) =
8628            recovered_macro_qualified_function_parameters(arguments, self.source)
8629        else {
8630            return;
8631        };
8632        let arity = parameter_labels.len();
8633        let function = FunctionInfo {
8634            package_name: scope.package_name.clone(),
8635            owner: Some(CppMemberOwner::Unit(parent.clone())),
8636            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
8637            signature,
8638        };
8639        if function.name.is_empty() {
8640            return;
8641        }
8642        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
8643        if self.parsed.contains_declaration(&code_unit) {
8644            self.link_source_declaration(ancestry, &code_unit, declaration_node);
8645            self.parsed
8646                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
8647            return;
8648        }
8649        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8650        let signature_label = render_cpp_function_display_signature_from_node(
8651            declaration_node,
8652            self.source,
8653            scope.template_signature.as_deref(),
8654            false,
8655            ancestry,
8656        );
8657        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
8658        // Only the callable's parameters came from the reparse: the
8659        // `field_declaration` this recovery reads is a real node in a real
8660        // class body, so its storage class and the body's access ladder state
8661        // this member's modifiers exactly as an ordinary member's do.
8662        let modifiers =
8663            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
8664        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
8665            .with_declaration_only(true)
8666            .with_callable_arity(CallableArity::exact(arity))
8667            .with_callable_linkage(linkage)
8668            .with_callable_modifiers(
8669                modifiers.is_static,
8670                modifiers.is_constructor,
8671                modifiers.visibility,
8672            );
8673        self.add_signature_with_metadata(code_unit.clone(), metadata);
8674        self.parsed.add_child(parent.clone(), code_unit);
8675    }
8676
8677    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
8678        &mut self,
8679        declaration_node: Node<'tree>,
8680        call: Node<'tree>,
8681        scope: &ScopeInfo,
8682        ancestry: &ParentIndex<'tree>,
8683    ) {
8684        let Some(parent) = &scope.class_unit else {
8685            return;
8686        };
8687        let Some(arguments) = call.child_by_field_name("arguments") else {
8688            return;
8689        };
8690        let Some((mut signature, parameter_labels)) =
8691            recovered_macro_qualified_function_parameters(arguments, self.source)
8692        else {
8693            return;
8694        };
8695        if let Some(template_signature) = &scope.template_signature {
8696            signature = format!("{template_signature}{signature}");
8697        }
8698        let arity = parameter_labels.len();
8699        let function = FunctionInfo {
8700            package_name: scope.package_name.clone(),
8701            owner: Some(CppMemberOwner::Unit(parent.clone())),
8702            name: parent.identifier().to_string(),
8703            signature,
8704        };
8705        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
8706        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8707        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
8708        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
8709        // The recovery already proved this member is the class's constructor:
8710        // the reparsed call names the enclosing class. Everything else is read
8711        // from the real `field_declaration` the recovery found it in.
8712        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
8713        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
8714            .with_declaration_only(false)
8715            .with_callable_arity(CallableArity::exact(arity))
8716            .with_callable_linkage(linkage)
8717            .with_callable_modifiers(
8718                modifiers.is_static,
8719                modifiers.is_constructor,
8720                modifiers.visibility,
8721            );
8722        self.add_signature_with_metadata(code_unit.clone(), metadata);
8723        self.parsed.add_child(parent.clone(), code_unit);
8724    }
8725
8726    fn visit_variable_declaration<'tree>(
8727        &mut self,
8728        declaration_node: Node<'tree>,
8729        declarator: Node<'tree>,
8730        scope: &ScopeInfo,
8731        in_class_body: bool,
8732        ancestry: &ParentIndex<'tree>,
8733    ) {
8734        let Some(name) = extract_variable_name(declarator, self.source) else {
8735            return;
8736        };
8737        let parent = if in_class_body {
8738            let Some(parent) = &scope.class_unit else {
8739                return;
8740            };
8741            Some(parent)
8742        } else {
8743            None
8744        };
8745        let short_name = match parent {
8746            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
8747            None => name.clone(),
8748        };
8749        let fq = cpp_leaf_fq(
8750            &scope.package_name,
8751            parent,
8752            &name,
8753            SegmentKind::Member,
8754            SegmentKind::Member,
8755        );
8756        let code_unit = CodeUnit::new_fq(
8757            self.file.clone(),
8758            CodeUnitType::Field,
8759            scope.package_name.clone(),
8760            short_name,
8761            fq,
8762        );
8763        if self.parsed.contains_declaration(&code_unit) {
8764            return;
8765        }
8766        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8767        self.add_signature_with_metadata(
8768            code_unit.clone(),
8769            SignatureMetadata::new(
8770                render_cpp_field_signature(declaration_node, declarator, self.source),
8771                Vec::new(),
8772            )
8773            .with_cpp_field_linkage(cpp_field_declaration_linkage(
8774                declaration_node,
8775                self.source,
8776                ancestry,
8777            )),
8778        );
8779        if let Some(parent) = &scope.class_unit {
8780            self.parsed.add_child(parent.clone(), code_unit);
8781        } else if let Some(module) = &scope.module {
8782            self.parsed.add_child(module.clone(), code_unit);
8783        }
8784    }
8785
8786    fn visit_class_members_from_declaration<'tree>(
8787        &mut self,
8788        node: Node<'tree>,
8789        scope: &ScopeInfo,
8790        ancestry: &ParentIndex<'tree>,
8791    ) {
8792        let mut cursor = node.walk();
8793        for child in node.named_children(&mut cursor) {
8794            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
8795                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
8796            } else if child.kind() == "init_declarator"
8797                && let Some(inner) = child.child_by_field_name("declarator")
8798            {
8799                self.visit_variable_declaration(node, inner, scope, true, ancestry);
8800            } else if matches!(
8801                child.kind(),
8802                "identifier"
8803                    | "field_identifier"
8804                    | "pointer_declarator"
8805                    | "reference_declarator"
8806                    | "array_declarator"
8807                    | "parenthesized_declarator"
8808            ) {
8809                self.visit_variable_declaration(node, child, scope, true, ancestry);
8810            }
8811        }
8812    }
8813
8814    fn visit_global_variables_from_declaration<'tree>(
8815        &mut self,
8816        node: Node<'tree>,
8817        scope: &ScopeInfo,
8818        ancestry: &ParentIndex<'tree>,
8819    ) {
8820        let mut cursor = node.walk();
8821        for child in node.named_children(&mut cursor) {
8822            if child.kind() == "init_declarator"
8823                && let Some(inner) = child.child_by_field_name("declarator")
8824            {
8825                self.visit_variable_declaration(node, inner, scope, false, ancestry);
8826            } else if matches!(
8827                child.kind(),
8828                "identifier"
8829                    | "field_identifier"
8830                    | "pointer_declarator"
8831                    | "reference_declarator"
8832                    | "array_declarator"
8833                    | "parenthesized_declarator"
8834            ) {
8835                self.visit_variable_declaration(node, child, scope, false, ancestry);
8836            }
8837        }
8838    }
8839
8840    fn visit_type_declaration<'tree>(
8841        &mut self,
8842        node: Node<'tree>,
8843        scope: &ScopeInfo,
8844        stack: &mut Vec<CppWork<'tree>>,
8845        ancestry: &ParentIndex<'tree>,
8846    ) {
8847        let type_node = node.child_by_field_name("type");
8848        if let Some(type_node) = type_node
8849            && matches!(
8850                type_node.kind(),
8851                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8852            )
8853        {
8854            self.visit_class_like(type_node, scope, stack, ancestry);
8855        }
8856
8857        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
8858            let range = Range {
8859                start_byte: node.start_byte(),
8860                end_byte: recovered.end_node.end_byte(),
8861                start_line: node.start_position().row + 1,
8862                end_line: recovered.end_node.end_position().row + 1,
8863            };
8864            let signature = self
8865                .source
8866                .get(range.start_byte..range.end_byte)
8867                .map(normalize_cpp_whitespace)
8868                .unwrap_or_default();
8869            self.record_type_aliases(
8870                ancestry,
8871                node,
8872                scope,
8873                vec![recovered.name],
8874                signature,
8875                range,
8876            );
8877            return;
8878        }
8879
8880        let alias_names = match node.kind() {
8881            "alias_declaration" => extract_alias_declaration_name(node, self.source)
8882                .into_iter()
8883                .collect::<Vec<_>>(),
8884            "type_definition" => extract_typedef_alias_names(node, self.source),
8885            _ => Vec::new(),
8886        };
8887        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
8888            (type_node, alias_names.as_slice())
8889            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
8890            && type_node.child_by_field_name("name").is_none()
8891        {
8892            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
8893        } else {
8894            None
8895        };
8896        self.add_type_aliases(ancestry, node, scope, alias_names);
8897        if let Some((body, alias_name)) = anonymous_aggregate {
8898            // The typedef alias is also the only user-visible identity of an
8899            // anonymous aggregate. Reuse it as the member owner instead of
8900            // minting a second signatureless class with the same FQN. The
8901            // latter makes forward lookup ambiguous when conditional aliases
8902            // coexist and returns duplicate definitions even without guards.
8903            let signature = normalize_cpp_whitespace(node_text(node, self.source));
8904            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
8905            debug_assert!(self.parsed.contains_declaration(&alias_unit));
8906            let mut nested_scope = scope.clone();
8907            nested_scope.class_unit = Some(alias_unit);
8908            nested_scope.template_signature = scope.template_signature.clone();
8909            nested_scope.template_metadata = None;
8910            nested_scope.declarations_are_fields = false;
8911            nested_scope.recovered_specialization_member_scope = false;
8912            stack.push(CppWork::Container(CppContainer {
8913                node: body,
8914                scope: nested_scope,
8915            }));
8916        }
8917    }
8918
8919    fn add_type_aliases(
8920        &mut self,
8921        ancestry: &ParentIndex<'_>,
8922        node: Node<'_>,
8923        scope: &ScopeInfo,
8924        alias_names: Vec<String>,
8925    ) {
8926        let signature = normalize_cpp_whitespace(node_text(node, self.source));
8927        self.record_type_aliases(
8928            ancestry,
8929            node,
8930            scope,
8931            alias_names,
8932            signature,
8933            cpp_declaration_range(node),
8934        );
8935    }
8936
8937    fn record_type_aliases(
8938        &mut self,
8939        ancestry: &ParentIndex<'_>,
8940        node: Node<'_>,
8941        scope: &ScopeInfo,
8942        alias_names: Vec<String>,
8943        signature: String,
8944        range: Range,
8945    ) {
8946        if signature.is_empty() {
8947            return;
8948        }
8949        let type_name = node
8950            .child_by_field_name("type")
8951            .and_then(|type_node| type_node.child_by_field_name("name"))
8952            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
8953        for alias_name in alias_names {
8954            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
8955                continue;
8956            }
8957            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
8958            // Declaration identity does not include the alias signature. Keep
8959            // each physical range so conditional aliases retain their guards.
8960            self.note_declaration(&code_unit);
8961            self.link_source_declaration(ancestry, &code_unit, node);
8962            self.parsed
8963                .add_code_unit_with_range(code_unit.clone(), range, None, None);
8964            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
8965            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
8966                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
8967            });
8968            self.add_signature_with_metadata(
8969                code_unit.clone(),
8970                SignatureMetadata::new(signature.clone(), Vec::new())
8971                    .with_underlying_type_identity(underlying_type_identity),
8972            );
8973            if let Some(metadata) = &scope.template_metadata {
8974                let mut metadata = metadata.clone();
8975                metadata.primary_fq_name = code_unit.fq_name();
8976                self.parsed
8977                    .set_cpp_template_metadata(code_unit.clone(), metadata);
8978            }
8979            if let Some(parent) = &scope.class_unit {
8980                self.parsed.add_child(parent.clone(), code_unit.clone());
8981            } else if let Some(module) = &scope.module {
8982                self.parsed.add_child(module.clone(), code_unit.clone());
8983            }
8984            self.parsed.mark_type_alias(code_unit);
8985        }
8986    }
8987
8988    fn type_alias_unit(
8989        &self,
8990        scope: &ScopeInfo,
8991        alias_name: String,
8992        signature: String,
8993    ) -> CodeUnit {
8994        let short_name = if let Some(parent) = &scope.class_unit {
8995            cpp_join_nested_short(parent.short_name(), &alias_name)
8996        } else {
8997            alias_name.clone()
8998        };
8999        let fq = cpp_leaf_fq(
9000            &scope.package_name,
9001            scope.class_unit.as_ref(),
9002            &alias_name,
9003            SegmentKind::Nested,
9004            SegmentKind::Type,
9005        );
9006        CodeUnit::with_signature_and_fq(
9007            self.file.clone(),
9008            CodeUnitType::Class,
9009            scope.package_name.clone(),
9010            short_name,
9011            Some(signature),
9012            false,
9013            fq,
9014        )
9015    }
9016
9017    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
9018        // A comment can terminate tree-sitter's preproc_arg before the logical
9019        // directive ends. Its remaining declarations are replacement locals,
9020        // not file-scope fields, even when recovery exposes them as siblings.
9021        if let Some(replacement) =
9022            crate::graph::syntax::function_macro_replacement_span(node, self.source)
9023        {
9024            self.consumed_fragment_regions
9025                .push((replacement.start, replacement.end));
9026        }
9027        let Some(name) = extract_macro_name(node, self.source) else {
9028            return;
9029        };
9030        let signature = node_text(node, self.source).trim_end().to_string();
9031        if signature.is_empty() {
9032            return;
9033        }
9034        let fq = cpp_member_fq("", &name);
9035        // A macro can be undefined and redefined later in the same file. Its
9036        // structured directive is part of the declaration identity so the
9037        // temporal environment can navigate to the definition active at a
9038        // reference instead of collapsing every spelling to the first range.
9039        // The same physical directive parsed through another C/C++ reading
9040        // still produces the same unit and remains deduplicated.
9041        let code_unit = CodeUnit::with_signature_and_fq(
9042            self.file.clone(),
9043            CodeUnitType::Macro,
9044            "",
9045            name.clone(),
9046            Some(signature.clone()),
9047            false,
9048            fq,
9049        );
9050        if !self.parsed.contains_declaration(&code_unit) {
9051            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
9052            let name_range = node
9053                .child_by_field_name("name")
9054                .map(cpp_declaration_range)
9055                .unwrap_or_else(|| cpp_declaration_range(node));
9056            self.parsed
9057                .record_materialization(MaterializationRecord::GeneratedDeclaration {
9058                    site: cpp_declaration_range(node),
9059                    argument: name_range,
9060                    kind: GenerationKind::PreprocessorDefinition,
9061                    unit: code_unit.clone(),
9062                });
9063            self.parsed.add_signature(code_unit, signature);
9064        }
9065        if node.kind() == "preproc_def" {
9066            update_object_macro_field_environment(
9067                node,
9068                self.source,
9069                &mut self.object_macro_fields,
9070                &mut self.ambiguous_object_macro_fields,
9071            );
9072        } else {
9073            self.object_macro_fields.remove(&name);
9074            self.ambiguous_object_macro_fields.remove(&name);
9075        }
9076    }
9077
9078    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9079        let Some(directive) = node.child_by_field_name("directive") else {
9080            return;
9081        };
9082        let name = node_text(directive, self.source).trim();
9083        let range = cpp_declaration_range(node);
9084        let fields = object_macro_field_closure(&self.object_macro_fields, name);
9085        self.materialize_object_macro_fields(fields, range, node, scope);
9086    }
9087
9088    /// Bare object-like field-list macros inside an otherwise well-formed
9089    /// aggregate remain identifier nodes in a field declaration. More than
9090    /// one adjacent invocation can be folded into the same declaration, so
9091    /// inspect all of its structured identifier nodes.
9092    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9093        if !matches!(node.kind(), "declaration" | "field_declaration") {
9094            return false;
9095        }
9096        // Claiming the declaration means its members are accounted for, which
9097        // is only true when there is an owner to materialize them into. A file
9098        // scope reaches this with a declaration that merely *contains* an
9099        // aggregate spelling an invocation (tree-sitter wraps libuv's
9100        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
9101        // `field_declaration` under a malformed header), and consuming it there
9102        // would discard the aggregate itself (#2985).
9103        if scope.class_unit.is_none() {
9104            return false;
9105        }
9106        let macro_nodes =
9107            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9108        for macro_node in &macro_nodes {
9109            let name = node_text(*macro_node, self.source).trim();
9110            let fields = object_macro_field_closure(&self.object_macro_fields, name);
9111            self.materialize_object_macro_fields(
9112                fields,
9113                cpp_declaration_range(*macro_node),
9114                *macro_node,
9115                scope,
9116            );
9117        }
9118        let Some(last) = macro_nodes.last() else {
9119            return false;
9120        };
9121        // Claiming the declaration accounts for every member it spells, not
9122        // only the donated ones: an invocation folded together with the
9123        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
9124        // leaves those members shapeless, and the grammar recovers them from
9125        // the byte range that follows the invocation.
9126        self.record_collapsed_aggregate_fields(
9127            last.end_byte()..node.end_byte(),
9128            node.start_position().row
9129                + 1
9130                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
9131            node,
9132            scope,
9133        );
9134        true
9135    }
9136
9137    /// Retain the grammar's field interpretation with the original invocation
9138    /// or aggregate context. The replacement AST owns the type, while only the
9139    /// original source owns activation, guards, and occurrence ranges.
9140    fn capture_recovered_field_facts(
9141        &mut self,
9142        unit: &CodeUnit,
9143        range: Range,
9144        context: Node<'_>,
9145        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
9146        names_function_type: bool,
9147    ) {
9148        let declaration = *self.source_links[unit]
9149            .last()
9150            .expect("recovered field follows its source occurrence");
9151        let mut fact = crate::source_facts::capture_declaration(
9152            declaration,
9153            context,
9154            unit,
9155            self.source,
9156            self.primary_ancestry,
9157        );
9158        fact.field_type = field_type;
9159        fact.names_function_type = names_function_type;
9160        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
9161        let captured =
9162            crate::source_context::capture_declaration_context(context, unit, self.source);
9163        fact.guard_requirements = captured.guard_requirements;
9164        fact.callable_guards = fact.guard_requirements.clone();
9165        fact.callable_activation = Some(range.end_byte);
9166        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
9167        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
9168        let mut intern = |node: Node<'_>| {
9169            let mut collector = self.source_collector.borrow_mut();
9170            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
9171                collector.intern_node(node)
9172            } else {
9173                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
9174            }
9175        };
9176        fact.conditional_family =
9177            crate::graph::resolver::preprocessor_conditional_family_node(context).map(&mut intern);
9178        fact.exhaustive_conditional_family =
9179            captured.exhaustive_conditional_family.map(&mut intern);
9180        fact.displaced_namespace_closing_brace =
9181            captured.displaced_namespace_closing_brace.map(&mut intern);
9182        self.capture_source_dependencies(&mut fact, context, unit);
9183        self.apply_active_source_context(&mut fact);
9184        assert!(
9185            self.source_properties
9186                .borrow_mut()
9187                .insert(declaration, fact)
9188                .is_none()
9189        );
9190    }
9191
9192    fn materialize_object_macro_fields(
9193        &mut self,
9194        fields: Vec<MacroReplacementField>,
9195        range: Range,
9196        context: Node<'_>,
9197        scope: &ScopeInfo,
9198    ) {
9199        let Some(owner) = scope.class_unit.as_ref() else {
9200            return;
9201        };
9202        for field in fields {
9203            let signature = field.declaration.clone();
9204            let mut fq = owner.fq().clone();
9205            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9206            let short_name = if owner.short_name().is_empty() {
9207                field.name.clone()
9208            } else {
9209                format!("{}.{}", owner.short_name(), field.name)
9210            };
9211            let code_unit = CodeUnit::with_signature_and_fq(
9212                self.file.clone(),
9213                CodeUnitType::Field,
9214                owner.package_name().to_string(),
9215                short_name,
9216                Some(field.declaration),
9217                true,
9218                fq,
9219            );
9220            if self.parsed.contains_declaration(&code_unit) {
9221                continue;
9222            }
9223            self.add_declaration_with_range(
9224                self.primary_ancestry,
9225                code_unit.clone(),
9226                range,
9227                None,
9228                Some(owner.clone()),
9229                None,
9230            );
9231            self.capture_recovered_field_facts(
9232                &code_unit,
9233                range,
9234                context,
9235                field.field_type,
9236                field.names_function_type,
9237            );
9238            self.parsed.add_signature(code_unit, signature);
9239        }
9240    }
9241
9242    /// Recover ordinary aggregates whose field-list macro invocations made
9243    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
9244    /// The class markers, aggregate braces, field nodes, and macro identifier
9245    /// nodes are all read from the AST; no source delimiter or token scan is
9246    /// used. A close-brace node inside a malformed field closes the innermost
9247    /// recovered aggregate, preserving nested-owner identity.
9248    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9249        let mut cursor = node.walk();
9250        let children = node.children(&mut cursor).collect::<Vec<_>>();
9251        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
9252        let mut object_macro_fields = self.object_macro_fields.clone();
9253        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
9254        let mut open = Vec::<usize>::new();
9255        let mut index = 0;
9256        while index < children.len() {
9257            let keyword = children[index];
9258            if update_object_macro_field_environment(
9259                keyword,
9260                self.source,
9261                &mut object_macro_fields,
9262                &mut ambiguous_object_macro_fields,
9263            ) {
9264                index += 1;
9265                continue;
9266            }
9267            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
9268                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9269                if !name.is_empty() {
9270                    let parent = open
9271                        .last()
9272                        .and_then(|class| recovered.get(*class))
9273                        .map(|(owner, _, _, _)| owner.clone())
9274                        .or_else(|| scope.class_unit.clone());
9275                    let short_name = parent.as_ref().map_or_else(
9276                        || name.clone(),
9277                        |parent| cpp_join_nested_short(parent.short_name(), &name),
9278                    );
9279                    let fq = cpp_leaf_fq(
9280                        &scope.package_name,
9281                        parent.as_ref(),
9282                        &name,
9283                        SegmentKind::Nested,
9284                        SegmentKind::Type,
9285                    );
9286                    let owner = CodeUnit::with_signature_and_fq(
9287                        self.file.clone(),
9288                        CodeUnitType::Class,
9289                        scope.package_name.clone(),
9290                        short_name,
9291                        None,
9292                        false,
9293                        fq,
9294                    );
9295                    recovered.push((
9296                        owner,
9297                        head.key.start_byte(),
9298                        head.opening.end_byte(),
9299                        Vec::new(),
9300                    ));
9301                    let class_index = recovered.len() - 1;
9302                    match head.folded_members {
9303                        // The ordinary shape leaves the aggregate open: every
9304                        // child that follows belongs to it until a close brace
9305                        // ends it.
9306                        None => open.push(class_index),
9307                        // A folded head carries its own member list and its own
9308                        // closing brace, so it closes here. The invocations that
9309                        // precede the class key are the dangling tail of the
9310                        // replacement that folded it, and belong to whatever
9311                        // aggregate is open around it, not to this one.
9312                        Some(members) => {
9313                            let macro_nodes = object_macro_identifier_nodes_with_environment(
9314                                children[index],
9315                                self.source,
9316                                &mut object_macro_fields,
9317                                &mut ambiguous_object_macro_fields,
9318                            );
9319                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9320                                .iter()
9321                                .partition(|node| node.start_byte() < head.key.start_byte());
9322                            if let Some(&enclosing) = open.last() {
9323                                for macro_node in preceding {
9324                                    recovered[enclosing]
9325                                        .3
9326                                        .push(CppCollapsedMember::MacroFields {
9327                                            context: macro_node,
9328                                            range: cpp_declaration_range(macro_node),
9329                                            fields: object_macro_field_closure(
9330                                                &object_macro_fields,
9331                                                &normalize_cpp_whitespace(node_text(
9332                                                    macro_node,
9333                                                    self.source,
9334                                                )),
9335                                            ),
9336                                        });
9337                                }
9338                            }
9339                            let closing = cpp_collapsed_aggregate_closing_brace(members);
9340                            recovered[class_index]
9341                                .3
9342                                .extend(cpp_collapsed_aggregate_members(
9343                                    members,
9344                                    &inner,
9345                                    head.opening.end_byte()..closing,
9346                                    head.opening.end_position().row + 1,
9347                                    self.source,
9348                                    &object_macro_fields,
9349                                ));
9350                            recovered[class_index].2 = members.end_byte();
9351                            for &open_class in &open {
9352                                recovered[open_class].2 =
9353                                    recovered[open_class].2.max(members.end_byte());
9354                            }
9355                        }
9356                    }
9357                    index += head.width;
9358                    continue;
9359                }
9360            }
9361            if let Some(&class_index) = open.last()
9362                && children[index].kind() == "field_declaration"
9363            {
9364                let field = children[index];
9365                let macro_nodes = object_macro_identifier_nodes_with_environment(
9366                    field,
9367                    self.source,
9368                    &mut object_macro_fields,
9369                    &mut ambiguous_object_macro_fields,
9370                );
9371                recovered[class_index]
9372                    .3
9373                    .extend(cpp_collapsed_aggregate_members(
9374                        field,
9375                        &macro_nodes,
9376                        field.start_byte()..field.end_byte(),
9377                        field.start_position().row + 1,
9378                        self.source,
9379                        &object_macro_fields,
9380                    ));
9381                let end = field.end_byte();
9382                for &open_class in &open {
9383                    recovered[open_class].2 = recovered[open_class].2.max(end);
9384                }
9385                let closes = count_close_brace_nodes(field);
9386                for _ in 0..closes {
9387                    if let Some(closed) = open.pop() {
9388                        recovered[closed].2 = end;
9389                    }
9390                }
9391            }
9392            index += 1;
9393        }
9394
9395        let mut owners = Vec::with_capacity(recovered.len());
9396        for (owner, start, end, members) in recovered {
9397            let parent = owners
9398                .iter()
9399                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
9400                .cloned()
9401                .or_else(|| scope.class_unit.clone());
9402            self.declare_collapsed_aggregate(
9403                owner.clone(),
9404                start..end,
9405                Some(node),
9406                parent,
9407                members,
9408                scope,
9409            );
9410            owners.push(owner);
9411        }
9412    }
9413
9414    /// Record one recovered aggregate and the members it declares.
9415    fn declare_collapsed_aggregate(
9416        &mut self,
9417        owner: CodeUnit,
9418        span: std::ops::Range<usize>,
9419        evidence: Option<Node<'_>>,
9420        parent: Option<CodeUnit>,
9421        members: Vec<CppCollapsedMember<'_>>,
9422        scope: &ScopeInfo,
9423    ) {
9424        let range = Range {
9425            start_byte: span.start,
9426            end_byte: span.end,
9427            start_line: self.source.get(..span.start).map_or(1, |source| {
9428                source.bytes().filter(|byte| *byte == b'\n').count() + 1
9429            }),
9430            end_line: self.source.get(..span.end).map_or(1, |source| {
9431                source.bytes().filter(|byte| *byte == b'\n').count() + 1
9432            }),
9433        };
9434        // The tag can already be declared by a typedef or a forward
9435        // declaration, and this range is the definition's. Record it either
9436        // way: a declaration carries every range it is written at, and
9437        // dropping this one leaves nothing able to prove that the aggregate
9438        // has a body (#3098). Recording a range that is already held, or a
9439        // unit that is already declared, changes nothing.
9440        self.add_declaration_with_range(
9441            self.primary_ancestry,
9442            owner.clone(),
9443            range,
9444            evidence,
9445            parent,
9446            None,
9447        );
9448        // The recovery admitted this owner from an aggregate head and body.
9449        // Its malformed outer node need not itself be a struct_specifier, so
9450        // retain that structured definition proof explicitly.
9451        let declaration = *self.source_links[&owner]
9452            .last()
9453            .expect("collapsed aggregate has a source occurrence");
9454        {
9455            let mut properties = self.source_properties.borrow_mut();
9456            let fact = properties
9457                .get_mut(&declaration)
9458                .expect("collapsed aggregate retains its original context");
9459            fact.class_strength =
9460                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
9461            fact.occurrence_role =
9462                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
9463        }
9464        let owner_scope = ScopeInfo {
9465            class_unit: Some(owner),
9466            declarations_are_fields: true,
9467            ..scope.clone()
9468        };
9469        for member in members {
9470            match member {
9471                CppCollapsedMember::MacroFields {
9472                    range,
9473                    fields,
9474                    context,
9475                } => {
9476                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
9477                }
9478                CppCollapsedMember::Declarations {
9479                    span,
9480                    start_line,
9481                    context,
9482                } => {
9483                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
9484                }
9485            }
9486        }
9487    }
9488
9489    /// Recover an aggregate whose head tree-sitter folded into the declaration
9490    /// that precedes it, outside any collapsed `ERROR` container.
9491    ///
9492    /// A comment inside a field-list macro's replacement ends the replacement
9493    /// token, so the rest of the replacement is read at file scope. When the
9494    /// aggregate that follows declares its member list with one invocation,
9495    /// the parser reads the dangling tail as that declaration's type, the
9496    /// class key as an `ERROR`, and the member list as an initializer (#3098).
9497    /// The invocations before the class key belong to the replacement, not to
9498    /// this aggregate, so they go to whatever aggregate encloses it.
9499    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9500        if node.parent().is_some_and(|parent| parent.is_error()) {
9501            // The container's own recovery reads this child in source order,
9502            // with the preprocessor environment its earlier children build.
9503            return false;
9504        }
9505        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
9506            return false;
9507        };
9508        let members = head
9509            .folded_members
9510            .expect("a folded aggregate head carries its member list");
9511        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9512        if name.is_empty() {
9513            return false;
9514        }
9515        let macro_nodes =
9516            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9517        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9518            .iter()
9519            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
9520        for macro_node in preceding {
9521            let fields = object_macro_field_closure(
9522                &self.object_macro_fields,
9523                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
9524            );
9525            self.materialize_object_macro_fields(
9526                fields,
9527                cpp_declaration_range(macro_node),
9528                macro_node,
9529                scope,
9530            );
9531        }
9532        let parent = scope.class_unit.clone();
9533        let short_name = parent.as_ref().map_or_else(
9534            || name.clone(),
9535            |parent| cpp_join_nested_short(parent.short_name(), &name),
9536        );
9537        let fq = cpp_leaf_fq(
9538            &scope.package_name,
9539            parent.as_ref(),
9540            &name,
9541            SegmentKind::Nested,
9542            SegmentKind::Type,
9543        );
9544        let owner = CodeUnit::with_signature_and_fq(
9545            self.file.clone(),
9546            CodeUnitType::Class,
9547            scope.package_name.clone(),
9548            short_name,
9549            None,
9550            false,
9551            fq,
9552        );
9553        let recovered = cpp_collapsed_aggregate_members(
9554            members,
9555            &inner,
9556            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
9557            head.opening.end_position().row + 1,
9558            self.source,
9559            &self.object_macro_fields,
9560        );
9561        self.declare_collapsed_aggregate(
9562            owner,
9563            head.key.start_byte()..members.end_byte(),
9564            Some(node),
9565            parent,
9566            recovered,
9567            scope,
9568        );
9569        true
9570    }
9571
9572    /// Record the members of a collapsed aggregate region as fields of its
9573    /// recovered owner. The names, declaration text, and byte ranges all come
9574    /// from the grammar's reading of the region, so a region that is not a
9575    /// member list contributes nothing.
9576    fn record_collapsed_aggregate_fields(
9577        &mut self,
9578        span: std::ops::Range<usize>,
9579        start_line: usize,
9580        context: Node<'_>,
9581        scope: &ScopeInfo,
9582    ) {
9583        let Some(owner) = scope.class_unit.as_ref() else {
9584            return;
9585        };
9586        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
9587            let range = Range {
9588                start_byte: field.range.start,
9589                end_byte: field.range.end,
9590                start_line: start_line
9591                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
9592                end_line: start_line
9593                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
9594            };
9595            let mut fq = owner.fq().clone();
9596            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9597            let short_name = if owner.short_name().is_empty() {
9598                field.name.clone()
9599            } else {
9600                format!("{}.{}", owner.short_name(), field.name)
9601            };
9602            let signature = normalize_cpp_whitespace(&field.declaration);
9603            let code_unit = CodeUnit::with_signature_and_fq(
9604                self.file.clone(),
9605                CodeUnitType::Field,
9606                owner.package_name().to_string(),
9607                short_name,
9608                Some(signature.clone()),
9609                false,
9610                fq,
9611            );
9612            if self.parsed.contains_declaration(&code_unit) {
9613                continue;
9614            }
9615            self.add_declaration_with_range(
9616                self.primary_ancestry,
9617                code_unit.clone(),
9618                range,
9619                None,
9620                Some(owner.clone()),
9621                None,
9622            );
9623            self.capture_recovered_field_facts(
9624                &code_unit,
9625                range,
9626                context,
9627                field.field_type,
9628                field.names_function_type,
9629            );
9630            self.parsed.add_signature(code_unit, signature);
9631        }
9632    }
9633
9634    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9635        let Some(_directive) = node.child_by_field_name("directive") else {
9636            return;
9637        };
9638        if is_cpp_undef_directive(node, self.source) {
9639            update_object_macro_field_environment(
9640                node,
9641                self.source,
9642                &mut self.object_macro_fields,
9643                &mut self.ambiguous_object_macro_fields,
9644            );
9645            return;
9646        }
9647        let directly_in_field_list = node
9648            .parent()
9649            .is_some_and(|parent| parent.kind() == "field_declaration_list");
9650        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
9651            self.visit_object_macro_fields(node, scope);
9652        }
9653    }
9654}
9655
9656/// Every member a field-list macro contributes under `environment`, including
9657/// the members of the field-list macros it composes.
9658///
9659/// Composition is resolved at the invocation, not at the definition, because a
9660/// nested name's active replacement is a property of the environment the
9661/// invocation sees. The traversal is iterative and visits each name once, so a
9662/// self-composing or mutually composing pair terminates.
9663fn object_macro_field_closure(
9664    environment: &HashMap<String, ObjectMacroReplacement>,
9665    name: &str,
9666) -> Vec<MacroReplacementField> {
9667    let mut fields = Vec::new();
9668    let mut visited = HashSet::default();
9669    let mut stack = vec![name.to_string()];
9670    while let Some(current) = stack.pop() {
9671        if !visited.insert(current.clone()) {
9672            continue;
9673        }
9674        let Some(replacement) = environment.get(&current) else {
9675            continue;
9676        };
9677        fields.extend(replacement.fields.iter().cloned());
9678        stack.extend(replacement.nested.iter().rev().cloned());
9679    }
9680    fields
9681}
9682
9683/// The structured replacement an object-like `#define` contributes.
9684///
9685/// The directive's whole logical line is the replacement list, which
9686/// [`object_macro_replacement_span`] recovers, because a comment inside the
9687/// replacement ends tree-sitter's `preproc_arg` token early.
9688fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
9689    crate::graph::syntax::object_macro_replacement_span(node, source)
9690        .and_then(|span| source.get(span))
9691        .map(crate::graph::syntax::object_macro_replacement)
9692        .unwrap_or_default()
9693}
9694
9695/// Apply one preprocessor directive to an object-like field-list environment.
9696/// The environment is intentionally separate from the declaration visitor so
9697/// malformed parser regions can replay directives in source order without
9698/// mutating the live walk's state ahead of those directives.
9699fn update_object_macro_field_environment(
9700    node: Node<'_>,
9701    source: &str,
9702    fields: &mut HashMap<String, ObjectMacroReplacement>,
9703    ambiguous: &mut HashSet<String>,
9704) -> bool {
9705    match node.kind() {
9706        "preproc_def" => {
9707            let Some(name) = extract_macro_name(node, source) else {
9708                return false;
9709            };
9710            let replacement = object_macro_replacement_of(node, source);
9711            if replacement.is_empty() || ambiguous.contains(&name) {
9712                fields.remove(&name);
9713                ambiguous.insert(name);
9714            } else if let Some(previous) = fields.get(&name) {
9715                if previous != &replacement {
9716                    fields.remove(&name);
9717                    ambiguous.insert(name);
9718                }
9719            } else {
9720                fields.insert(name, replacement);
9721            }
9722            true
9723        }
9724        "preproc_call" if is_cpp_undef_directive(node, source) => {
9725            if let Some(argument) = node.child_by_field_name("argument") {
9726                let name = node_text(argument, source).trim();
9727                fields.remove(name);
9728                if inside_preprocessor_conditional(node) {
9729                    ambiguous.insert(name.to_string());
9730                } else {
9731                    ambiguous.remove(name);
9732                }
9733            }
9734            true
9735        }
9736        _ => false,
9737    }
9738}
9739
9740fn object_macro_identifier_nodes<'tree>(
9741    node: Node<'tree>,
9742    source: &str,
9743    fields: &HashMap<String, ObjectMacroReplacement>,
9744) -> Vec<Node<'tree>> {
9745    let mut result = Vec::new();
9746    let mut stack = vec![node];
9747    while let Some(current) = stack.pop() {
9748        if matches!(
9749            current.kind(),
9750            "identifier" | "field_identifier" | "type_identifier"
9751        ) && fields.contains_key(node_text(current, source).trim())
9752        {
9753            result.push(current);
9754        }
9755        let mut cursor = current.walk();
9756        let children = current.children(&mut cursor).collect::<Vec<_>>();
9757        stack.extend(children.into_iter().rev());
9758    }
9759    result.sort_by_key(|node| node.start_byte());
9760    result
9761}
9762
9763/// Collect macro identifiers while replaying any nested preprocessor
9764/// directives in source order. Recovery regions can contain a later `#undef`
9765/// in the same parser error envelope; using the live visitor map for the
9766/// entire envelope would incorrectly materialize invocations after it.
9767fn object_macro_identifier_nodes_with_environment<'tree>(
9768    node: Node<'tree>,
9769    source: &str,
9770    fields: &mut HashMap<String, ObjectMacroReplacement>,
9771    ambiguous: &mut HashSet<String>,
9772) -> Vec<Node<'tree>> {
9773    let mut result = Vec::new();
9774    let mut stack = vec![node];
9775    while let Some(current) = stack.pop() {
9776        if update_object_macro_field_environment(current, source, fields, ambiguous) {
9777            continue;
9778        }
9779        if matches!(
9780            current.kind(),
9781            "identifier" | "field_identifier" | "type_identifier"
9782        ) && fields.contains_key(node_text(current, source).trim())
9783        {
9784            result.push(current);
9785        }
9786        let mut cursor = current.walk();
9787        let children = current.children(&mut cursor).collect::<Vec<_>>();
9788        stack.extend(children.into_iter().rev());
9789    }
9790    result.sort_by_key(|node| node.start_byte());
9791    result
9792}
9793
9794/// One member region of an aggregate whose head and body tree-sitter collapsed
9795/// into an `ERROR` container, in source order.
9796enum CppCollapsedMember<'tree> {
9797    /// The members a field-list macro invocation donates, all sharing the
9798    /// invocation's range.
9799    MacroFields {
9800        context: Node<'tree>,
9801        range: Range,
9802        fields: Vec<MacroReplacementField>,
9803    },
9804    /// A byte span whose ordinary member declarations lost their shape with
9805    /// the aggregate body, with the 1-based line the span starts on.
9806    Declarations {
9807        context: Node<'tree>,
9808        span: std::ops::Range<usize>,
9809        start_line: usize,
9810    },
9811}
9812
9813/// One aggregate head recovered from the children of a collapsed `ERROR`
9814/// container.
9815///
9816/// A member list that opens with an object-like field-list macro invocation
9817/// has no grammar rule, so the aggregate never forms a specifier and its head
9818/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
9819/// class key, the name, and the opening brace are three consecutive children.
9820/// When the aggregate follows the dangling tail of a comment-truncated
9821/// replacement, the parser reads that tail as the declaration's type and folds
9822/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
9823/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
9824/// }` that follows becomes one `field_declaration` whose class key is an
9825/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
9826/// `initializer_list` (#3098).
9827struct CppCollapsedAggregateHead<'tree> {
9828    key: Node<'tree>,
9829    name: Node<'tree>,
9830    opening: Node<'tree>,
9831    /// The member list when the head folded it into its own node, which also
9832    /// carries the aggregate's closing brace. `None` leaves the aggregate open
9833    /// for the children that follow it.
9834    folded_members: Option<Node<'tree>>,
9835    /// How many children the head consumes.
9836    width: usize,
9837}
9838
9839/// The aggregate head that starts at `children[index]`, if one does.
9840fn cpp_collapsed_aggregate_head<'tree>(
9841    children: &[Node<'tree>],
9842    index: usize,
9843    source: &str,
9844) -> Option<CppCollapsedAggregateHead<'tree>> {
9845    let key = *children.get(index)?;
9846    if matches!(key.kind(), "struct" | "class" | "union") {
9847        let name = *children.get(index + 1)?;
9848        let opening = *children.get(index + 2)?;
9849        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
9850            return None;
9851        }
9852        return Some(CppCollapsedAggregateHead {
9853            key,
9854            name,
9855            opening,
9856            folded_members: None,
9857            width: 3,
9858        });
9859    }
9860    cpp_folded_aggregate_head(key, source)
9861}
9862
9863/// The aggregate head tree-sitter folded into one declaration node, reading
9864/// the class key as an `ERROR` and the member list as an initializer.
9865fn cpp_folded_aggregate_head<'tree>(
9866    node: Node<'tree>,
9867    source: &str,
9868) -> Option<CppCollapsedAggregateHead<'tree>> {
9869    if !matches!(node.kind(), "declaration" | "field_declaration") {
9870        return None;
9871    }
9872    let mut cursor = node.walk();
9873    let children = node.children(&mut cursor).collect::<Vec<_>>();
9874    let key_index = children.iter().position(|child| {
9875        child.is_error()
9876            && matches!(
9877                node_text(*child, source).trim(),
9878                "struct" | "class" | "union"
9879            )
9880    })?;
9881    let declarator = *children.get(key_index + 1)?;
9882    // At file scope the grammar has an `init_declarator` for the name and its
9883    // initializer; inside an `ERROR` container the two are loose siblings.
9884    let (name, members) = if declarator.kind() == "init_declarator" {
9885        (
9886            declarator.child_by_field_name("declarator")?,
9887            declarator.child_by_field_name("value")?,
9888        )
9889    } else {
9890        (declarator, *children.get(key_index + 2)?)
9891    };
9892    if !matches!(
9893        name.kind(),
9894        "field_identifier" | "type_identifier" | "identifier"
9895    ) {
9896        return None;
9897    }
9898    if members.kind() != "initializer_list" {
9899        return None;
9900    }
9901    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
9902    Some(CppCollapsedAggregateHead {
9903        key: children[key_index],
9904        name,
9905        opening,
9906        folded_members: Some(members),
9907        width: 1,
9908    })
9909}
9910
9911/// Where a folded member list ends: at its own closing brace when it has one,
9912/// so the reparse of the declarations it holds sees a member list and not a
9913/// stray brace.
9914fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
9915    let mut cursor = members.walk();
9916    members
9917        .children(&mut cursor)
9918        .filter(|child| child.kind() == "}" && !child.is_missing())
9919        .last()
9920        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
9921}
9922
9923/// The members one collapsed aggregate region declares, in source order: what
9924/// its field-list macro invocations donate, then the ordinary declarations
9925/// that follow the last invocation and lost their shape with the body.
9926fn cpp_collapsed_aggregate_members<'tree>(
9927    context: Node<'tree>,
9928    macro_nodes: &[Node<'tree>],
9929    region: std::ops::Range<usize>,
9930    region_start_line: usize,
9931    source: &str,
9932    environment: &HashMap<String, ObjectMacroReplacement>,
9933) -> Vec<CppCollapsedMember<'tree>> {
9934    let mut members = macro_nodes
9935        .iter()
9936        .map(|macro_node| CppCollapsedMember::MacroFields {
9937            context: *macro_node,
9938            range: cpp_declaration_range(*macro_node),
9939            fields: object_macro_field_closure(
9940                environment,
9941                &normalize_cpp_whitespace(node_text(*macro_node, source)),
9942            ),
9943        })
9944        .collect::<Vec<_>>();
9945    // Whatever follows the last invocation is an ordinary member list that
9946    // lost its declaration shape with the aggregate's body; the grammar
9947    // recovers it from its own byte range.
9948    let declarations_start = macro_nodes
9949        .last()
9950        .map_or(region.start, |macro_node| macro_node.end_byte());
9951    members.push(CppCollapsedMember::Declarations {
9952        context,
9953        span: declarations_start..region.end,
9954        start_line: region_start_line
9955            + cpp_line_breaks_between(source, region.start, declarations_start),
9956    });
9957    members
9958}
9959
9960/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
9961/// line without rescanning the file from its start.
9962fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
9963    source.get(from..to).map_or(0, |slice| {
9964        slice.bytes().filter(|byte| *byte == b'\n').count()
9965    })
9966}
9967
9968/// How many enclosing aggregates a collapsed member declaration closes: the
9969/// close braces it carries that its own braces do not balance. A member that
9970/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
9971/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
9972/// owner early and lost every member after it (#2985).
9973fn count_close_brace_nodes(node: Node<'_>) -> usize {
9974    let mut opened = 0usize;
9975    let mut closed = 0usize;
9976    let mut stack = vec![node];
9977    while let Some(current) = stack.pop() {
9978        if !current.is_missing() {
9979            match current.kind() {
9980                "{" => opened += 1,
9981                "}" => closed += 1,
9982                _ => {}
9983            }
9984        }
9985        let mut cursor = current.walk();
9986        stack.extend(current.children(&mut cursor));
9987    }
9988    closed.saturating_sub(opened)
9989}
9990
9991/// Classify a C++ field while its declaration syntax is already available.
9992///
9993/// The persisted result lets later visibility queries avoid reparsing the
9994/// complete source file only to recover linkage.
9995pub fn cpp_field_declaration_linkage<'tree>(
9996    declaration: Node<'tree>,
9997    source: &str,
9998    ancestry: &ParentIndex<'tree>,
9999) -> CppFieldLinkage {
10000    let mut current = ancestry.parent(declaration);
10001    let mut enclosed_by_class = false;
10002    while let Some(node) = current {
10003        if node.kind() == "namespace_definition"
10004            && node
10005                .child_by_field_name("name")
10006                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10007        {
10008            return CppFieldLinkage::Internal;
10009        }
10010        if matches!(
10011            node.kind(),
10012            "class_specifier" | "struct_specifier" | "union_specifier"
10013        ) && node
10014            .child_by_field_name("name")
10015            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10016        {
10017            return CppFieldLinkage::Internal;
10018        }
10019        if matches!(
10020            node.kind(),
10021            "class_specifier" | "struct_specifier" | "union_specifier"
10022        ) {
10023            enclosed_by_class = true;
10024        }
10025        if matches!(node.kind(), "function_definition" | "lambda_expression") {
10026            return CppFieldLinkage::Internal;
10027        }
10028        current = ancestry.parent(node);
10029    }
10030    if enclosed_by_class {
10031        return CppFieldLinkage::External;
10032    }
10033    let mut cursor = declaration.walk();
10034    let mut has_static = false;
10035    let mut has_extern = false;
10036    let mut has_inline = false;
10037    let mut has_const = false;
10038    let mut has_constexpr = false;
10039    for child in declaration.named_children(&mut cursor) {
10040        let text = normalize_cpp_whitespace(node_text(child, source));
10041        match (child.kind(), text.as_str()) {
10042            ("storage_class_specifier", "static") => has_static = true,
10043            ("storage_class_specifier", "extern") => has_extern = true,
10044            ("storage_class_specifier", "inline") => has_inline = true,
10045            ("storage_class_specifier", "constexpr") => has_constexpr = true,
10046            ("type_qualifier", "const") => has_const = true,
10047            ("type_qualifier", "constexpr") => has_constexpr = true,
10048            _ => {}
10049        }
10050    }
10051    if has_static {
10052        CppFieldLinkage::Internal
10053    } else if has_extern || has_inline {
10054        CppFieldLinkage::External
10055    } else if has_const || has_constexpr {
10056        CppFieldLinkage::InternalUnlessExternalPeer
10057    } else {
10058        CppFieldLinkage::External
10059    }
10060}
10061
10062fn cpp_declaration_range(node: Node<'_>) -> Range {
10063    Range {
10064        start_byte: node.start_byte(),
10065        end_byte: node.end_byte(),
10066        start_line: node.start_position().row + 1,
10067        end_line: node.end_position().row + 1,
10068    }
10069}
10070
10071/// A recovery interval as a [`Range`], for materialization records whose
10072/// window is a byte region rather than one parser node (the sentinel-macro
10073/// region reparses, issue #941/#1657).
10074fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
10075    let line_at = |byte: usize| {
10076        source.as_bytes()[..byte]
10077            .iter()
10078            .filter(|&&b| b == b'\n')
10079            .count()
10080            + 1
10081    };
10082    Range {
10083        start_byte,
10084        end_byte,
10085        start_line: line_at(start_byte),
10086        end_line: line_at(end_byte),
10087    }
10088}
10089
10090fn cpp_include_info(raw_snippet: String) -> ImportInfo {
10091    ImportInfo {
10092        raw_snippet,
10093        is_wildcard: false,
10094        is_global: false,
10095        identifier: None,
10096        alias: None,
10097        path: None,
10098        binder_span: None,
10099    }
10100}
10101
10102fn recover_quoted_source_imports(
10103    source: &str,
10104    imports: &mut Vec<SourceImportFact>,
10105    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
10106    collector: &RefCell<PrimarySourceFactCollector<'_>>,
10107) {
10108    let mut in_block_comment = false;
10109    let mut start = 0;
10110    for line in source.split_inclusive('\n') {
10111        let end = start + line.len();
10112        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
10113        let trimmed = stripped.trim();
10114        if looks_like_quoted_include_line(trimmed) {
10115            let raw = normalize_cpp_whitespace(trimmed);
10116            if !imports.iter().any(|import| import.statement == raw) {
10117                let occurrence = collector.borrow_mut().intern_subspan_bytes(
10118                    start,
10119                    end,
10120                    SourceOccurrenceProvenance::ExplicitSubspan,
10121                );
10122                // Recovery already identified a quoted directive hidden by a
10123                // malformed outer construct. Interpret its path with the same
10124                // grammar in an isolated included range, preserving byte identity.
10125                let region =
10126                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
10127                let target = region.as_ref().and_then(|tree| {
10128                    let mut pending = vec![tree.root_node()];
10129                    while let Some(node) = pending.pop() {
10130                        if node.kind() == "preproc_include"
10131                            && let Some(path) = node.child_by_field_name("path")
10132                            && path.kind() == "string_literal"
10133                        {
10134                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
10135                            let target = collector.borrow_mut().intern_embedded(
10136                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
10137                            );
10138                            includes.push(
10139                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
10140                                    declaration: occurrence,
10141                                    target,
10142                                    path: value.to_owned(),
10143                                    quoted: true,
10144                                },
10145                            );
10146                            return Some(target);
10147                        }
10148                        let mut cursor = node.walk();
10149                        let children: Vec<_> = node.named_children(&mut cursor).collect();
10150                        pending.extend(children.into_iter().rev());
10151                    }
10152                    None
10153                });
10154                imports.push(SourceImportFact::from_import(
10155                    cpp_include_info(raw),
10156                    occurrence,
10157                    target,
10158                    None,
10159                    Vec::new(),
10160                ));
10161            }
10162        }
10163        start = end;
10164    }
10165}
10166
10167fn looks_like_quoted_include_line(line: &str) -> bool {
10168    let Some(rest) = line.trim_start().strip_prefix('#') else {
10169        return false;
10170    };
10171    let Some(rest) = rest.trim_start().strip_prefix("include") else {
10172        return false;
10173    };
10174    rest.trim_start().starts_with('"')
10175}
10176
10177fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
10178    let mut raw = Vec::new();
10179    if let Some(bases) = cpp_class_like_base_clause(node) {
10180        collect_cpp_base_nodes(bases, source, &mut raw);
10181    }
10182    raw
10183}
10184
10185fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
10186    let mut facts = Vec::new();
10187    let mut cursor = node.walk();
10188    for child in node.named_children(&mut cursor) {
10189        if child.kind() != "base_class_clause" {
10190            continue;
10191        }
10192        let mut is_virtual = false;
10193        let mut child_cursor = child.walk();
10194        for base in child.children(&mut child_cursor) {
10195            match base.kind() {
10196                "virtual" => is_virtual = true,
10197                "," => is_virtual = false,
10198                "type_identifier"
10199                | "qualified_identifier"
10200                | "scoped_type_identifier"
10201                | "template_type"
10202                | "template_function" => {
10203                    if let Some(fact) =
10204                        crate::source_facts::base_specifier_from_node(base, source, is_virtual)
10205                    {
10206                        facts.push(fact);
10207                    }
10208                    is_virtual = false;
10209                }
10210                _ => {}
10211            }
10212        }
10213    }
10214    facts
10215}
10216
10217/// The `base_class_clause` a class-like node writes, if it writes one.
10218///
10219/// The pinned grammar exposes the clause as a named child rather than a field,
10220/// so the question is asked by kind. A base clause is the only place a C++
10221/// class body records that some other class may declare a member, which is
10222/// what [`cpp_callable_dispatch_extensibility`] reads it for.
10223fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
10224    let mut cursor = class_like.walk();
10225    class_like
10226        .named_children(&mut cursor)
10227        .find(|child| child.kind() == "base_class_clause")
10228}
10229
10230fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
10231    walk_named_tree_preorder(node, false, |child| match child.kind() {
10232        "type_identifier" | "qualified_identifier" | "template_type" => {
10233            let text = normalize_cpp_whitespace(node_text(child, source));
10234            if !text.is_empty() {
10235                raw.push(text);
10236            }
10237            WalkControl::SkipChildren
10238        }
10239        _ => WalkControl::Continue,
10240    });
10241}
10242
10243fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
10244    let mut out = String::new();
10245    let chars: Vec<char> = line.chars().collect();
10246    let mut index = 0;
10247    let mut in_string = false;
10248    let mut in_char = false;
10249    let mut escape = false;
10250
10251    while index < chars.len() {
10252        let ch = chars[index];
10253        let next = chars.get(index + 1).copied();
10254
10255        if *in_block_comment {
10256            if ch == '*' && next == Some('/') {
10257                *in_block_comment = false;
10258                index += 2;
10259            } else {
10260                index += 1;
10261            }
10262            continue;
10263        }
10264
10265        if in_string {
10266            out.push(ch);
10267            if escape {
10268                escape = false;
10269            } else if ch == '\\' {
10270                escape = true;
10271            } else if ch == '"' {
10272                in_string = false;
10273            }
10274            index += 1;
10275            continue;
10276        }
10277
10278        if in_char {
10279            out.push(ch);
10280            if escape {
10281                escape = false;
10282            } else if ch == '\\' {
10283                escape = true;
10284            } else if ch == '\'' {
10285                in_char = false;
10286            }
10287            index += 1;
10288            continue;
10289        }
10290
10291        if ch == '/' && next == Some('/') {
10292            break;
10293        }
10294        if ch == '/' && next == Some('*') {
10295            *in_block_comment = true;
10296            index += 2;
10297            continue;
10298        }
10299        if ch == '"' {
10300            in_string = true;
10301            out.push(ch);
10302            index += 1;
10303            continue;
10304        }
10305        if ch == '\'' {
10306            in_char = true;
10307            out.push(ch);
10308            index += 1;
10309            continue;
10310        }
10311
10312        out.push(ch);
10313        index += 1;
10314    }
10315
10316    out
10317}
10318
10319#[derive(Clone)]
10320struct FunctionInfo {
10321    package_name: String,
10322    owner: Option<CppMemberOwner>,
10323    name: String,
10324    signature: String,
10325}
10326
10327/// Owner of a member function, kept structured so a literal `$` inside a
10328/// source-level class name never crosses a join/split boundary: the legacy
10329/// `$`-joined owner string was re-split at fq construction, dropping a leading
10330/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
10331/// and tripping the package/short boundary assert -- the #2140 corruption one
10332/// level up (#2362).
10333#[derive(Clone)]
10334enum CppMemberOwner {
10335    /// Source-level owner class chain from a qualified declarator-id, one
10336    /// class name per component (`Outer::Inner::method` -> `["Outer",
10337    /// "Inner"]`); each component may itself contain a literal `$`.
10338    Chain(Vec<String>),
10339    /// The lexically enclosing or recovered class unit; the member fq extends
10340    /// its fq directly instead of re-splitting its `$`-joined short chain.
10341    Unit(CodeUnit),
10342}
10343
10344impl CppMemberOwner {
10345    /// The legacy `$`-joined owner chain used in the member's short name.
10346    fn short_chain(&self) -> String {
10347        match self {
10348            Self::Chain(chain) => chain.join("$"),
10349            Self::Unit(parent) => parent.short_name().to_string(),
10350        }
10351    }
10352}
10353
10354enum DeclaratorKind<'a> {
10355    Function(Node<'a>),
10356    Variable(Node<'a>),
10357}
10358
10359impl FunctionInfo {
10360    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
10361        self.code_unit_with_synthetic(file, false)
10362    }
10363
10364    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
10365        let short_name = match &self.owner {
10366            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
10367            None => self.name.clone(),
10368        };
10369        let fq = match &self.owner {
10370            Some(CppMemberOwner::Chain(chain)) => {
10371                debug_assert!(
10372                    !chain.is_empty(),
10373                    "an empty owner chain is no owner; producers return None instead"
10374                );
10375                let mut fq = FqName::new();
10376                cpp_push_package(&mut fq, &self.package_name);
10377                let mut first = true;
10378                for component in chain {
10379                    let kind = if first {
10380                        SegmentKind::Type
10381                    } else {
10382                        SegmentKind::Nested
10383                    };
10384                    fq.push(cpp_segment(component, kind));
10385                    first = false;
10386                }
10387                fq.push(cpp_segment(&self.name, SegmentKind::Member));
10388                fq
10389            }
10390            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
10391                .fq()
10392                .clone()
10393                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
10394            // An anonymous parent (empty short chain) contributes no owner
10395            // segment -- the same guard as cpp_join_member_short above.
10396            Some(CppMemberOwner::Unit(_)) | None => {
10397                let mut fq = FqName::new();
10398                cpp_push_package(&mut fq, &self.package_name);
10399                fq.push(cpp_segment(&self.name, SegmentKind::Member));
10400                fq
10401            }
10402        };
10403        CodeUnit::with_signature_and_fq(
10404            file,
10405            CodeUnitType::Function,
10406            self.package_name.clone(),
10407            short_name,
10408            Some(self.signature.clone()),
10409            synthetic,
10410            fq,
10411        )
10412    }
10413}
10414
10415fn extract_function_info(
10416    declarator: Node<'_>,
10417    source: &str,
10418    scope: &ScopeInfo,
10419) -> Option<FunctionInfo> {
10420    let parameters_node = declarator.child_by_field_name("parameters")?;
10421    let declarator_name_node = declarator
10422        .child_by_field_name("declarator")
10423        .or_else(|| parameters_node.prev_named_sibling())?;
10424    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
10425}
10426
10427fn extract_function_info_from_name(
10428    declarator: Node<'_>,
10429    declarator_name_node: Node<'_>,
10430    source: &str,
10431    scope: &ScopeInfo,
10432) -> Option<FunctionInfo> {
10433    let parameters_node = declarator.child_by_field_name("parameters")?;
10434    let parameters_text = cpp_parameter_signature(parameters_node, source);
10435    let recovered_specialization_member = scope
10436        .recovered_specialization_member_scope
10437        .then(|| {
10438            let terminal = declarator_name_node
10439                .child_by_field_name("name")
10440                .unwrap_or(declarator_name_node);
10441            let name = canonical_cpp_qualified_component(terminal, source)?.name;
10442            let owner = scope.class_unit.as_ref()?;
10443            Some((
10444                Some(CppMemberOwner::Unit(owner.clone())),
10445                name,
10446                scope.package_name.clone(),
10447            ))
10448        })
10449        .flatten();
10450    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
10451        parts
10452    } else if let Some(parts) =
10453        split_structured_templated_cpp_name(declarator_name_node, source, scope)
10454    {
10455        parts
10456    } else {
10457        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
10458            declarator_name_node,
10459            source,
10460        )?);
10461        if raw_name.is_empty() {
10462            return None;
10463        }
10464        split_cpp_name(&raw_name, scope)
10465    };
10466    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
10467    let mut signature = if suffix.is_empty() {
10468        parameters_text
10469    } else {
10470        format!("{parameters_text} {suffix}")
10471    };
10472    if let Some(template_signature) = &scope.template_signature {
10473        signature = format!("{template_signature}{signature}");
10474    }
10475
10476    Some(FunctionInfo {
10477        package_name,
10478        owner,
10479        name,
10480        signature,
10481    })
10482}
10483
10484/// Recover the semantic return type and callable name when a declaration macro
10485/// occupies a function definition's `type` field. Tree-sitter either exposes a
10486/// scalar return as the declarator's apparent name and the callable as the sole
10487/// identifier in an `ERROR`, or joins a template return and callable into a
10488/// qualified identifier with a missing `::`. Both shapes retain the complete
10489/// parameter list and body; a concrete separator remains an out-of-line member.
10490fn cpp_macro_displaced_callable_parts<'tree>(
10491    function_declarator: Node<'tree>,
10492    source: &str,
10493    ancestry: &ParentIndex<'tree>,
10494) -> Option<(Node<'tree>, Node<'tree>)> {
10495    let definition = ancestry.parent(function_declarator)?;
10496    if definition.kind() != "function_definition"
10497        || definition.child_by_field_name("declarator") != Some(function_declarator)
10498        || definition
10499            .child_by_field_name("body")
10500            .is_none_or(|body| body.kind() != "compound_statement")
10501    {
10502        return None;
10503    }
10504    let macro_type = definition.child_by_field_name("type")?;
10505    if macro_type.kind() != "type_identifier"
10506        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10507    {
10508        return None;
10509    }
10510
10511    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
10512    if apparent_return_type.kind() == "qualified_identifier"
10513        && let (Some(return_type), Some(callable_name)) = (
10514            apparent_return_type.child_by_field_name("scope"),
10515            apparent_return_type.child_by_field_name("name"),
10516        )
10517        && return_type.kind() == "template_type"
10518        && matches!(callable_name.kind(), "identifier" | "field_identifier")
10519        && (0..apparent_return_type.child_count())
10520            .filter_map(|index| apparent_return_type.child(index))
10521            .any(|child| child.kind() == "::" && child.is_missing())
10522        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
10523        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10524    {
10525        return Some((return_type, callable_name));
10526    }
10527    if !matches!(
10528        apparent_return_type.kind(),
10529        "identifier" | "field_identifier" | "type_identifier"
10530    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
10531    {
10532        return None;
10533    }
10534    let parameters = function_declarator.child_by_field_name("parameters")?;
10535    let mut cursor = function_declarator.walk();
10536    let between = function_declarator
10537        .named_children(&mut cursor)
10538        .filter(|child| child.kind() != "comment")
10539        .filter(|child| {
10540            child.start_byte() >= apparent_return_type.end_byte()
10541                && child.end_byte() <= parameters.start_byte()
10542                && !same_node(*child, apparent_return_type)
10543                && !same_node(*child, parameters)
10544        })
10545        .collect::<Vec<_>>();
10546    let [name_error] = between.as_slice() else {
10547        return None;
10548    };
10549    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
10550        return None;
10551    }
10552    let callable_name = name_error.named_child(0)?;
10553    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
10554        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10555    {
10556        return None;
10557    }
10558    Some((apparent_return_type, callable_name))
10559}
10560
10561/// The part of a `function_declarator` after its parameter list that belongs to
10562/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
10563/// specification, a trailing return type and a trailing requires-clause.
10564///
10565/// The grammar makes each of these a distinct sibling of the `parameters`
10566/// field, so they are read from the tree. Splitting the declarator's text on
10567/// the parameter list instead silently dropped every qualifier whenever the
10568/// parameter list was spelled with whitespace that normalization rewrote - a
10569/// line break or a double space was enough to make a `const` member definition
10570/// a different logical symbol from its declaration (#1827).
10571///
10572/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
10573/// are deliberately excluded. C++ does not make them part of the signature and
10574/// an out-of-line definition never repeats them, so including them would split
10575/// a declaration from its own definition.
10576fn cpp_declarator_identity_suffix(
10577    declarator: Node<'_>,
10578    parameters_node: Node<'_>,
10579    source: &str,
10580) -> String {
10581    let mut cursor = declarator.walk();
10582    let parts = declarator
10583        .named_children(&mut cursor)
10584        .filter(|child| child.start_byte() >= parameters_node.end_byte())
10585        .filter(|child| {
10586            matches!(
10587                child.kind(),
10588                "type_qualifier"
10589                    | "ref_qualifier"
10590                    | "noexcept"
10591                    | "throw_specifier"
10592                    | "trailing_return_type"
10593                    | "requires_clause"
10594            )
10595        })
10596        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10597        .filter(|text| !text.is_empty())
10598        .collect::<Vec<_>>();
10599    normalize_cpp_qualifier_suffix(&parts.join(" "))
10600}
10601
10602/// The identity suffix of one callable declarator, for a consumer that holds
10603/// the declarator rather than the declaration walk's parts.
10604///
10605/// The persisted signature concatenates the parameter spelling and this suffix,
10606/// so a comparison that must agree on the suffix alone recomputes it here
10607/// instead of splitting the stored string.
10608pub(crate) fn cpp_callable_identity_suffix(
10609    function_declarator: Node<'_>,
10610    source: &str,
10611) -> Option<String> {
10612    let parameters_node = function_declarator.child_by_field_name("parameters")?;
10613    Some(cpp_declarator_identity_suffix(
10614        function_declarator,
10615        parameters_node,
10616        source,
10617    ))
10618}
10619
10620pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
10621    match classify_declarator(node)? {
10622        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
10623        DeclaratorKind::Variable(_) => None,
10624    }
10625}
10626
10627fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
10628    match node.kind() {
10629        "function_declarator" => {
10630            let inner = node
10631                .child_by_field_name("declarator")
10632                .or_else(|| node.child_by_field_name("name"))
10633                .or_else(|| last_named_child(node));
10634            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
10635                Some(DeclaratorKind::Variable(node))
10636            } else {
10637                Some(DeclaratorKind::Function(node))
10638            }
10639        }
10640        "init_declarator"
10641        | "pointer_declarator"
10642        | "reference_declarator"
10643        | "parenthesized_declarator"
10644        | "array_declarator"
10645        | "attributed_declarator"
10646        | "template_function" => node
10647            .child_by_field_name("declarator")
10648            .or_else(|| node.child_by_field_name("name"))
10649            .or_else(|| last_named_child(node))
10650            .and_then(classify_declarator),
10651        "identifier" | "field_identifier" | "qualified_identifier" => {
10652            Some(DeclaratorKind::Variable(node))
10653        }
10654        _ => node
10655            .child_by_field_name("declarator")
10656            .or_else(|| node.child_by_field_name("name"))
10657            .or_else(|| last_named_child(node))
10658            .and_then(classify_declarator),
10659    }
10660}
10661
10662fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
10663    matches!(
10664        node.kind(),
10665        "function_declarator"
10666            | "init_declarator"
10667            | "pointer_declarator"
10668            | "reference_declarator"
10669            | "parenthesized_declarator"
10670            | "array_declarator"
10671            | "attributed_declarator"
10672            | "template_function"
10673            | "identifier"
10674            | "field_identifier"
10675            | "qualified_identifier"
10676    )
10677}
10678
10679fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
10680    let class_like = first_class_like_child(node);
10681    let mut cursor = node.walk();
10682    node.named_children(&mut cursor).any(|child| {
10683        matches!(
10684            child.kind(),
10685            "init_declarator"
10686                | "pointer_declarator"
10687                | "reference_declarator"
10688                | "array_declarator"
10689                | "function_declarator"
10690                | "parenthesized_declarator"
10691                | "attributed_declarator"
10692        ) || matches!(
10693            child.kind(),
10694            "identifier" | "field_identifier" | "qualified_identifier"
10695        ) && class_like.is_none_or(|class_node| {
10696            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
10697        })
10698    })
10699}
10700
10701/// One namespace-scope forward class declaration that a recovered export-macro
10702/// class definition may borrow its identity from.  Tree-sitter can close a
10703/// malformed class at the enclosing namespace's closing brace, leaving the later
10704/// class definitions as root-level recovered `function_definition` nodes.  A
10705/// preceding `class Name;` in the same namespace is the only structured identity
10706/// signal available in that shape.
10707///
10708/// Everything recorded here is a property of the forward declaration alone.
10709/// What depends on the node doing the asking -- that the forward and its
10710/// namespace both close before it, with nothing but recovery trivia between --
10711/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
10712/// tree answers every later question about it.
10713struct CppNamespaceForward {
10714    name: String,
10715    start_byte: usize,
10716    /// Where the malformed namespace that held the forward ended.  No query
10717    /// asks anything else about that node.
10718    namespace_end_byte: usize,
10719    package_name: String,
10720}
10721
10722/// Read `node` as a borrowable namespace forward declaration.
10723///
10724/// The admission is deliberately conservative: only a body-less class specifier
10725/// whose declaration has no declarator, at namespace scope rather than inside a
10726/// function or class body, in a namespace that itself failed to parse.
10727fn cpp_namespace_forward_entry<'tree>(
10728    node: Node<'tree>,
10729    source: &str,
10730    ancestry: &ParentIndex<'tree>,
10731) -> Option<CppNamespaceForward> {
10732    if !matches!(
10733        node.kind(),
10734        "class_specifier" | "struct_specifier" | "union_specifier"
10735    ) || cpp_body_node(node).is_some()
10736    {
10737        return None;
10738    }
10739    let parent = node.parent()?;
10740    if !(parent.kind() == "declaration_list"
10741        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
10742    {
10743        return None;
10744    }
10745    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
10746    // Borrowing is only justified by the parser-recovery shape we are
10747    // repairing: the namespace that held the forward must itself contain a
10748    // syntax error. A clean, unrelated namespace forward is not an identity
10749    // proof.
10750    if !namespace.has_error() {
10751        return None;
10752    }
10753    Some(CppNamespaceForward {
10754        name: class_like_name(node, source, ancestry)?,
10755        start_byte: node.start_byte(),
10756        namespace_end_byte: namespace.end_byte(),
10757        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
10758    })
10759}
10760
10761/// Whether `forward` stands in the recovery relation to the node asking about
10762/// it: it and its malformed namespace both closed before the recovered class,
10763/// and nothing but recovery trivia separates the two.
10764fn cpp_namespace_forward_matches_recovery(
10765    forward: &CppNamespaceForward,
10766    recovered_node: Node<'_>,
10767) -> bool {
10768    forward.start_byte < recovered_node.start_byte()
10769        && forward.namespace_end_byte < recovered_node.start_byte()
10770        && malformed_namespace_is_nearest_recovery_region(
10771            forward.namespace_end_byte,
10772            recovered_node,
10773        )
10774}
10775
10776/// What one open [`CppVisitor::record_recovered_declarations`] has watched
10777/// happen to the declaration set.
10778///
10779/// The recovered set used to be a difference against a clone of the whole
10780/// declaration set, taken once per recovery: O(recoveries x declarations) on
10781/// exactly the error-recovered files that already walk slowest (#2787). The
10782/// walk knows which declarations it creates, so the capture collects them as
10783/// they are made and the difference is never needed.
10784///
10785/// `removed_pre_existing` is what makes that equal to the difference. A
10786/// deferred replacement removes the replaced declaration's children
10787/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
10788/// re-creates them. Creation alone cannot tell that apart from a first mint, so
10789/// a removal of a declaration this capture did not create records that it was
10790/// already there when the capture opened.
10791#[derive(Debug, Default)]
10792pub struct CppRecoveryCapture {
10793    /// Declarations created while this capture was open, in creation order.
10794    created: Vec<CodeUnit>,
10795    /// Membership for `created`.
10796    created_units: HashSet<CodeUnit>,
10797    /// Declarations that predate this capture and have been removed during it.
10798    removed_pre_existing: HashSet<CodeUnit>,
10799}
10800
10801/// Which owners the parse product already holds field declarations for, folded
10802/// in as the walk records them.
10803///
10804/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
10805/// and used to answer it by scanning every declaration accumulated so far:
10806/// O(enums x declarations) on exactly the generated headers that declare many
10807/// of both (#2786). The answer only grows by declaration, so the walk carries
10808/// it. A field's short name names its owner chain, `Owner.member`, so one field
10809/// answers for every dotted prefix of its own short name; an anonymous enum's
10810/// or union's enumerators carry bare short names instead (#2140), which is what
10811/// an empty owner short name asks about.
10812#[derive(Debug, Default)]
10813pub struct CppFieldOwnerIndex {
10814    /// Package name -> the owner short names its fields name.
10815    owners: HashMap<String, HashSet<String>>,
10816    /// Packages holding at least one field that names no owner.
10817    ownerless_packages: HashSet<String>,
10818}
10819
10820impl CppFieldOwnerIndex {
10821    /// The index of the declarations recorded so far, built when the first
10822    /// question arrives.
10823    fn of<'unit>(
10824        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
10825        file: &ProjectFile,
10826    ) -> Self {
10827        let mut index = Self::default();
10828        for declaration in declarations {
10829            index.record(declaration, file);
10830        }
10831        index
10832    }
10833
10834    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
10835        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
10836            return;
10837        }
10838        let short_name = code_unit.short_name();
10839        let package_name = code_unit.package_name();
10840        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
10841            self.ownerless_packages.insert(package_name.to_string());
10842        }
10843        if !short_name.contains('.') {
10844            return;
10845        }
10846        if !self.owners.contains_key(package_name) {
10847            self.owners
10848                .insert(package_name.to_string(), HashSet::default());
10849        }
10850        let owners = self
10851            .owners
10852            .get_mut(package_name)
10853            .expect("the package entry was just ensured");
10854        for (offset, _) in short_name.match_indices('.') {
10855            let owner = &short_name[..offset];
10856            if !owners.contains(owner) {
10857                owners.insert(owner.to_string());
10858            }
10859        }
10860    }
10861
10862    /// Whether a field declaration in `package_name` names `owner_short_name`
10863    /// as its owner. An empty owner asks about ownerless fields instead.
10864    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
10865        if owner_short_name.is_empty() {
10866            self.ownerless_packages.contains(package_name)
10867        } else {
10868            self.owners
10869                .get(package_name)
10870                .is_some_and(|owners| owners.contains(owner_short_name))
10871        }
10872    }
10873}
10874
10875/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
10876/// debug build asserts every carried answer against and as the release-mode
10877/// parity tests' reference (#2786).
10878#[cfg(any(debug_assertions, test))]
10879fn cpp_declarations_hold_owned_fields<'unit>(
10880    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
10881    file: &ProjectFile,
10882    package_name: &str,
10883    owner_short_name: &str,
10884) -> bool {
10885    let prefix = format!("{owner_short_name}.");
10886    declarations.into_iter().any(|unit| {
10887        unit.kind() == CodeUnitType::Field
10888            && unit.source() == file
10889            && unit.package_name() == package_name
10890            && if owner_short_name.is_empty() {
10891                // Anonymous enum/union parent: its enumerators carry bare
10892                // short names (#2140), so presence means any ownerless
10893                // field in this file.
10894                !unit.short_name().contains(['.', '$'])
10895            } else {
10896                unit.short_name().starts_with(&prefix)
10897            }
10898    })
10899}
10900
10901/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
10902///
10903/// A region reparse is its own tree and is dropped while the walk that made it
10904/// continues, so a later parse can be allocated at the same address and hand out
10905/// the same node ids.  The root's span and shape pin the identity its address
10906/// alone does not: two roots agreeing on all of this are the same parse of the
10907/// same bytes, and a scan of one is a scan of the other.
10908#[derive(PartialEq, Eq, Hash)]
10909pub struct CppTreeIdentity {
10910    root_id: usize,
10911    start_byte: usize,
10912    end_byte: usize,
10913    kind_id: u16,
10914    child_count: usize,
10915}
10916
10917impl CppTreeIdentity {
10918    fn of(root: Node<'_>) -> Self {
10919        Self {
10920            root_id: root.id(),
10921            start_byte: root.start_byte(),
10922            end_byte: root.end_byte(),
10923            kind_id: root.kind_id(),
10924            child_count: root.child_count(),
10925        }
10926    }
10927}
10928
10929/// The namespace forward declarations one tree's prefix holds, folded in as the
10930/// walk asks about them.
10931///
10932/// `scope_for_recovered_exported_class` asks the same question once per
10933/// recovered class, and the answer depends only on the part of the tree that
10934/// starts before the asking node.  Rescanning that prefix per question is
10935/// quadratic in the file, and a generated header whose parse recovery leaves
10936/// thousands of class-like declarations at file scope pays all of it: 1,904
10937/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
10938/// (#2754).  This carries the scan forward instead.  Each question advances the
10939/// traversal from wherever the last one stopped to the asking node's start byte,
10940/// so a whole walk pays at most one pass over the prefix its furthest question
10941/// reaches, and each question then costs a name lookup.
10942#[derive(Default)]
10943pub struct CppNamespaceForwardScan {
10944    /// Every named node starting before this byte has been folded in.
10945    scanned_through: usize,
10946    forwards: HashMap<String, Vec<CppNamespaceForward>>,
10947}
10948
10949impl CppNamespaceForwardScan {
10950    /// Fold in every named node of `root` that starts at or after the fold
10951    /// watermark and before `cutoff`.
10952    ///
10953    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
10954    /// pass owes are exactly the ones no earlier pass reached, and a question
10955    /// about an earlier byte than one already answered costs nothing.
10956    fn advance_to<'tree>(
10957        &mut self,
10958        root: Node<'tree>,
10959        cutoff: usize,
10960        source: &str,
10961        ancestry: &ParentIndex<'tree>,
10962    ) {
10963        if cutoff <= self.scanned_through {
10964            return;
10965        }
10966        let folded_through = self.scanned_through;
10967        let mut cursor = root.walk();
10968        let mut stack = vec![root];
10969        while let Some(current) = stack.pop() {
10970            if (folded_through..cutoff).contains(&current.start_byte())
10971                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
10972            {
10973                self.forwards
10974                    .entry(forward.name.clone())
10975                    .or_default()
10976                    .push(forward);
10977            }
10978            // A subtree ending before the watermark holds only nodes an earlier
10979            // pass already folded, and one starting at or after the cutoff is
10980            // outside the prefix being asked about. Skipping both is what keeps
10981            // the total traversal to one pass.
10982            for child in current.named_children(&mut cursor) {
10983                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
10984                    stack.push(child);
10985                }
10986            }
10987        }
10988        self.scanned_through = cutoff;
10989    }
10990
10991    /// The one namespace `name` was forward declared in before `recovered_node`.
10992    /// More than one matching forward declaration is ambiguous and answers
10993    /// nothing rather than guessing.
10994    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
10995        let mut matching = self
10996            .forwards
10997            .get(name)
10998            .into_iter()
10999            .flatten()
11000            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
11001        let first = matching.next()?;
11002        matching
11003            .next()
11004            .is_none()
11005            .then(|| first.package_name.clone())
11006    }
11007}
11008
11009/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
11010/// debug build checks every answer against (and the one the parity tests drive
11011/// directly).  It walks the whole prefix per question, which is exactly the cost
11012/// #2754 removed from the release path.
11013#[cfg(any(debug_assertions, test))]
11014fn unique_earlier_cpp_namespace_forward<'tree>(
11015    recovered_node: Node<'tree>,
11016    name: &str,
11017    source: &str,
11018    ancestry: &ParentIndex<'tree>,
11019) -> Option<String> {
11020    let mut root = recovered_node;
11021    while let Some(parent) = ancestry.parent(root) {
11022        root = parent;
11023    }
11024
11025    let mut candidates = Vec::new();
11026    let mut stack = vec![root];
11027    while let Some(current) = stack.pop() {
11028        if current.start_byte() < recovered_node.start_byte()
11029            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11030            && forward.name == name
11031            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
11032        {
11033            candidates.push(forward.package_name);
11034        }
11035
11036        let mut cursor = current.walk();
11037        for child in current.named_children(&mut cursor) {
11038            if child.start_byte() < recovered_node.start_byte() {
11039                stack.push(child);
11040            }
11041        }
11042    }
11043
11044    if candidates.len() == 1 {
11045        candidates.pop()
11046    } else {
11047        None
11048    }
11049}
11050
11051fn malformed_namespace_is_nearest_recovery_region(
11052    namespace_end_byte: usize,
11053    recovered_node: Node<'_>,
11054) -> bool {
11055    let mut root = recovered_node;
11056    while let Some(parent) = root.parent() {
11057        root = parent;
11058    }
11059    let mut cursor = root.walk();
11060    root.named_children(&mut cursor)
11061        .filter(|sibling| {
11062            namespace_end_byte <= sibling.start_byte()
11063                && sibling.end_byte() <= recovered_node.start_byte()
11064        })
11065        .all(is_malformed_namespace_recovery_trivia)
11066}
11067
11068fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
11069    matches!(node.kind(), "ERROR" | "comment")
11070        || node.kind().starts_with("preproc_")
11071        || node.kind() == "expression_statement" && node.named_child_count() == 0
11072}
11073
11074/// Return the namespace path for a forward class only when the declaration is
11075/// at namespace scope.  A declaration nested in a function/class body may share
11076/// the same namespace ancestor but cannot identify a top-level class definition.
11077fn cpp_namespace_name_for_forward<'tree>(
11078    node: Node<'tree>,
11079    source: &str,
11080    ancestry: &ParentIndex<'tree>,
11081) -> Option<String> {
11082    cpp_namespace_definition_for_forward(node, ancestry)?;
11083    cpp_lexical_namespace_name(node, source, ancestry)
11084}
11085
11086fn cpp_namespace_definition_for_forward<'tree>(
11087    node: Node<'tree>,
11088    ancestry: &ParentIndex<'tree>,
11089) -> Option<Node<'tree>> {
11090    let declaration = ancestry.parent(node)?;
11091    let mut ancestor = ancestry.parent(declaration);
11092    while let Some(current) = ancestor {
11093        if matches!(
11094            current.kind(),
11095            "compound_statement"
11096                | "field_declaration_list"
11097                | "class_specifier"
11098                | "struct_specifier"
11099                | "union_specifier"
11100                | "function_definition"
11101                | "lambda_expression"
11102        ) {
11103            return None;
11104        }
11105        if current.kind() == "namespace_definition" {
11106            return Some(current);
11107        }
11108        ancestor = ancestry.parent(current);
11109    }
11110    None
11111}
11112
11113fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
11114    match node.kind() {
11115        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11116        "parenthesized_declarator" => node
11117            .child_by_field_name("declarator")
11118            .or_else(|| last_named_child(node))
11119            .is_some_and(is_pointer_wrapper_declarator),
11120        "template_function" => node
11121            .child_by_field_name("name")
11122            .is_some_and(is_function_pointer_like_inner_declarator),
11123        _ => false,
11124    }
11125}
11126
11127fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
11128    match node.kind() {
11129        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11130        "parenthesized_declarator" => node
11131            .child_by_field_name("declarator")
11132            .or_else(|| last_named_child(node))
11133            .is_some_and(is_pointer_wrapper_declarator),
11134        _ => false,
11135    }
11136}
11137
11138fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
11139    let cleaned = raw_name.trim_start_matches("template ").trim();
11140    // A leading `::` is the explicit-global marker, not an empty owner segment.
11141    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
11142    // erroneous macro envelope swallowing the first identifier of an
11143    // out-of-line `X::X` constructor, chromium #1573); without this strip the
11144    // split below yields owner_parts `[""]`, constructing a unit with an empty
11145    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
11146    let cleaned = cleaned.trim_start_matches("::");
11147    // Parser recovery can preserve two adjacent scope operators around a
11148    // missing component (for example `X::/**/::method` in compiler diagnostic
11149    // fixtures). Empty components are syntax-recovery artifacts, never C++
11150    // owners. Keeping one as the final owner constructed `short_name=".method"`
11151    // and violated the structured package/short boundary during a large LLVM
11152    // workspace build. This is the same legacy-string-to-FqName bridge as the
11153    // ordinary split above; discard only components that the delimiter itself
11154    // proves empty.
11155    let parts: Vec<_> = cleaned
11156        .split("::")
11157        .filter(|component| !component.is_empty())
11158        .collect();
11159    if parts.is_empty() {
11160        return (None, cleaned.to_string(), scope.package_name.clone());
11161    }
11162    if parts.len() > 1 {
11163        let name = parts.last().unwrap_or(&cleaned).to_string();
11164        let owner_parts = &parts[..parts.len() - 1];
11165        if let Some(class_unit) = &scope.class_unit {
11166            // Lexically inside a class body: the owner is that class, whatever
11167            // the declarator re-qualifies it as.
11168            return (
11169                Some(CppMemberOwner::Unit(class_unit.clone())),
11170                name,
11171                scope.package_name.clone(),
11172            );
11173        }
11174        if !scope.package_name.is_empty() {
11175            // Out-of-line member definition written *inside* an enclosing
11176            // `namespace {}` block (scope package is that namespace). Every
11177            // owner segment before the terminal member is a class-nesting step
11178            // -- an out-of-line nested-class member `Outer::Inner::method` in
11179            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
11180            // namespace path: `using namespace` never brings nested-class
11181            // access into unqualified scope, so C++ always writes the full
11182            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
11183            // that redundantly re-states the enclosing namespace it already
11184            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
11185            // strip that re-qualifying prefix (which duplicates a suffix of the
11186            // enclosing package path) before treating what remains as the
11187            // nested-class chain, so the redundant spelling still lands on the
11188            // same `log4cxx.Foo.method` identity as its header declaration.
11189            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
11190            let owner = (!nested.is_empty()).then(|| {
11191                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
11192            });
11193            return (owner, name, scope.package_name.clone());
11194        }
11195        // File scope (no enclosing `namespace {}` block, scope package empty).
11196        let (owner, package_name) = if owner_parts.len() > 1 {
11197            // A multi-segment qualifier at file scope with no enclosing
11198            // namespace: treat all but the last owner segment as the namespace
11199            // path and the last as the owning class (`ns1::ns2::Class::method`
11200            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
11201            // is really a namespace or an outer class cannot be told from the
11202            // declarator text alone here, and no enclosing namespace or
11203            // in-index owner is available at per-file extraction to confirm the
11204            // class reading, so the far-more-common namespace interpretation is
11205            // kept rather than guessed away (the nested-class-at-file-scope and
11206            // using-directive-qualified nested-class shapes remain on this
11207            // behavior; see #1121).
11208            (
11209                Some(CppMemberOwner::Chain(vec![
11210                    owner_parts.last().unwrap_or(&"").to_string(),
11211                ])),
11212                owner_parts[..owner_parts.len() - 1].join("::"),
11213            )
11214        } else {
11215            // A bare `Class::member` qualifier at file scope carries no
11216            // namespace segment of its own. The declarator alone cannot say
11217            // which namespace owns `Class` -- but a `using namespace X;`
11218            // directive already in effect at this point in the file (#1093,
11219            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
11220            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
11221            // is the remaining structural signal for it, so fall back to it
11222            // rather than leaving the definition's package empty while its
11223            // header declaration (parsed inside the `namespace {}` block) keeps
11224            // the real one -- an identity split that made the same member
11225            // unresolvable under its own displayed spelling.
11226            (
11227                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
11228                cpp_using_directive_namespace_for_bare_owner(scope),
11229            )
11230        };
11231        return (owner, name, package_name);
11232    }
11233
11234    let package_name = scope.package_name.clone();
11235    let owner = scope
11236        .class_unit
11237        .as_ref()
11238        .map(|parent| CppMemberOwner::Unit(parent.clone()));
11239    (owner, cleaned.to_string(), package_name)
11240}
11241
11242/// Drop the leading owner segments of an out-of-line member qualifier that
11243/// merely re-state the enclosing namespace the definition already sits in, so
11244/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
11245/// definition may redundantly write `a::b::Outer::Inner::method` (or the
11246/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
11247/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
11248/// noise, not class-nesting steps. Returns the owner segments with the longest
11249/// such re-qualifying prefix removed (possibly all of them, when the qualifier
11250/// names only the enclosing namespace before the terminal member -- a
11251/// re-qualified free function). `package_name` is the enclosing namespace path
11252/// in its stored `::`-joined form; both sides are split on the same delimiter
11253/// the namespace walker joined them with, so this compares namespace *segments*
11254/// rather than scanning text.
11255fn strip_redundant_namespace_prefix<'a>(
11256    owner_parts: &'a [&'a str],
11257    package_name: &str,
11258) -> &'a [&'a str] {
11259    if package_name.is_empty() {
11260        return owner_parts;
11261    }
11262    let package_segments: Vec<&str> = package_name.split("::").collect();
11263    let max_prefix = owner_parts.len().min(package_segments.len());
11264    for prefix_len in (1..=max_prefix).rev() {
11265        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
11266        if &owner_parts[..prefix_len] == package_suffix {
11267            return &owner_parts[prefix_len..];
11268        }
11269    }
11270    owner_parts
11271}
11272
11273/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
11274/// class name at file/namespace scope, from the `using namespace` directives
11275/// visible at this point in the file. Several may be in scope at once (a
11276/// primary `using namespace NS;` alongside deeper conveniences like `using
11277/// namespace NS::helpers;`); since the declarator gives no way to tell which
11278/// one actually declares the owner class, prefer the shallowest (fewest
11279/// `::`-separated segments) as the file's most likely "home" namespace,
11280/// breaking ties by declaration order. Returns an empty string (leaving the
11281/// caller's package unqualified, as before) when no using-namespace directive
11282/// is in scope.
11283fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
11284    scope
11285        .visible_using_namespaces
11286        .iter()
11287        .min_by_key(|namespace| namespace.split("::").count())
11288        .cloned()
11289        .unwrap_or_default()
11290}
11291
11292struct CppQualifiedNameComponent {
11293    name: String,
11294    is_template_id: bool,
11295}
11296
11297/// Canonical nested-class chain for an out-of-line class definition written
11298/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
11299/// component per class (`["Outer", "Inner"]`).
11300///
11301/// The enclosing namespace fixes the namespace/class boundary: after an
11302/// optional redundant spelling of that namespace, every component belongs to
11303/// the class chain. File-scope qualified class names remain untouched because
11304/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
11305///
11306/// The components stay structured (rather than being `$`-joined here) so the
11307/// fq construction can push one Type/Nested segment per class; the `$`-joined
11308/// short-name display form is derived at the call sites that need it.
11309fn qualified_class_name_chain(
11310    class_node: Node<'_>,
11311    source: &str,
11312    scope: &ScopeInfo,
11313) -> Option<Vec<String>> {
11314    if scope.package_name.is_empty() || scope.class_unit.is_some() {
11315        return None;
11316    }
11317    let name = class_node.child_by_field_name("name")?;
11318    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
11319    if explicitly_global
11320        || components.len() < 2
11321        || components.iter().any(|component| component.is_template_id)
11322    {
11323        return None;
11324    }
11325    let names = components
11326        .iter()
11327        .map(|component| component.name.as_str())
11328        .collect::<Vec<_>>();
11329    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
11330    if class_chain.is_empty() {
11331        return None;
11332    }
11333    Some(class_chain.iter().map(|name| name.to_string()).collect())
11334}
11335
11336fn structured_cpp_qualified_components(
11337    qualified_name: Node<'_>,
11338    source: &str,
11339) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
11340    if qualified_name.kind() != "qualified_identifier" {
11341        return None;
11342    }
11343
11344    let mut components = Vec::new();
11345    let mut current = qualified_name;
11346    let mut explicitly_global = false;
11347    loop {
11348        if current.kind() == "qualified_identifier" {
11349            if let Some(component) = current.child_by_field_name("scope") {
11350                components.push(canonical_cpp_qualified_component(component, source)?);
11351            } else if components.is_empty() {
11352                explicitly_global = true;
11353            } else {
11354                return None;
11355            }
11356            current = current.child_by_field_name("name")?;
11357        } else {
11358            components.push(canonical_cpp_qualified_component(current, source)?);
11359            break;
11360        }
11361    }
11362    Some((components, explicitly_global))
11363}
11364
11365fn split_structured_templated_cpp_name(
11366    declarator_name: Node<'_>,
11367    source: &str,
11368    scope: &ScopeInfo,
11369) -> Option<(Option<CppMemberOwner>, String, String)> {
11370    let (mut components, explicitly_global) =
11371        structured_cpp_qualified_components(declarator_name, source)?;
11372
11373    let terminal = components.pop()?;
11374    let owner_start = components
11375        .iter()
11376        .position(|component| component.is_template_id)?;
11377    let explicit_package = components[..owner_start]
11378        .iter()
11379        .map(|component| component.name.as_str())
11380        .collect::<Vec<_>>()
11381        .join("::");
11382    let explicit_package_is_empty = explicit_package.is_empty();
11383    let package_name = match (
11384        explicitly_global,
11385        scope.package_name.is_empty(),
11386        explicit_package_is_empty,
11387    ) {
11388        (true, _, _) => explicit_package,
11389        (false, _, true) => scope.package_name.clone(),
11390        (false, true, false) => explicit_package,
11391        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
11392    };
11393    // Same identity-split fallback as `split_cpp_name` (#1093): a template
11394    // specialization's owner class named with no namespace segment of its own
11395    // (`explicit_package` empty) at file scope (`explicitly_global` false)
11396    // with nothing enclosing (`package_name` still empty) has no structural
11397    // signal for its namespace besides an in-scope `using namespace X;`.
11398    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
11399    {
11400        cpp_using_directive_namespace_for_bare_owner(scope)
11401    } else {
11402        package_name
11403    };
11404    let owner_chain = components[owner_start..]
11405        .iter()
11406        .map(|component| component.name.clone())
11407        .collect::<Vec<_>>();
11408    if owner_chain.is_empty() || terminal.name.is_empty() {
11409        return None;
11410    }
11411
11412    Some((
11413        Some(CppMemberOwner::Chain(owner_chain)),
11414        terminal.name,
11415        package_name,
11416    ))
11417}
11418
11419fn canonical_cpp_qualified_component(
11420    mut component: Node<'_>,
11421    source: &str,
11422) -> Option<CppQualifiedNameComponent> {
11423    let mut is_template_id = false;
11424    loop {
11425        match component.kind() {
11426            "template_type" => {
11427                is_template_id = true;
11428                component = component.child_by_field_name("name")?;
11429            }
11430            "dependent_name" => component = component.named_child(0)?,
11431            "identifier"
11432            | "field_identifier"
11433            | "namespace_identifier"
11434            | "type_identifier"
11435            | "operator_name"
11436            | "destructor_name" => {
11437                let name = normalize_cpp_whitespace(node_text(component, source));
11438                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
11439                    name,
11440                    is_template_id,
11441                });
11442            }
11443            _ => component = component.child_by_field_name("name")?,
11444        }
11445    }
11446}
11447
11448fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
11449    if let Some(name) = macro_decorated_unqualified_name(node) {
11450        return extract_declarator_name(name, source);
11451    }
11452    match node.kind() {
11453        "identifier"
11454        | "field_identifier"
11455        | "type_identifier"
11456        | "operator_name"
11457        | "destructor_name"
11458        | "qualified_identifier" => node_text(node, source).to_string(),
11459        "function_declarator"
11460        | "pointer_declarator"
11461        | "reference_declarator"
11462        | "parenthesized_declarator"
11463        | "array_declarator"
11464        | "template_function" => node
11465            .child_by_field_name("declarator")
11466            .or_else(|| node.child_by_field_name("name"))
11467            .or_else(|| last_named_child(node))
11468            .map(|child| extract_declarator_name(child, source))
11469            .unwrap_or_else(|| node_text(node, source).to_string()),
11470        _ => node
11471            .child_by_field_name("name")
11472            .map(|child| extract_declarator_name(child, source))
11473            .unwrap_or_else(|| node_text(node, source).to_string()),
11474    }
11475}
11476
11477/// Extract a callable identity only through declaration-shaped AST nodes.
11478/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
11479/// expose the call's parameter list as a false function declarator; accepting
11480/// arbitrary node text there emitted bogus names such as `.*f`.
11481fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11482    if let Some(name) = macro_decorated_unqualified_name(node) {
11483        return extract_callable_declarator_name(name, source);
11484    }
11485    match node.kind() {
11486        "identifier"
11487        | "field_identifier"
11488        | "type_identifier"
11489        | "operator_name"
11490        | "destructor_name"
11491        | "qualified_identifier" => Some(node_text(node, source).to_string()),
11492        "function_declarator"
11493        | "pointer_declarator"
11494        | "reference_declarator"
11495        | "parenthesized_declarator"
11496        | "array_declarator"
11497        | "template_function" => node
11498            .child_by_field_name("declarator")
11499            .or_else(|| node.child_by_field_name("name"))
11500            .and_then(|child| extract_callable_declarator_name(child, source)),
11501        _ => None,
11502    }
11503}
11504
11505fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
11506    match node.kind() {
11507        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
11508            let name = node_text(node, source).trim().to_string();
11509            (!name.is_empty()).then_some(name)
11510        }
11511        _ => node
11512            .child_by_field_name("declarator")
11513            .or_else(|| node.child_by_field_name("name"))
11514            .or_else(|| last_named_child(node))
11515            .and_then(|child| extract_variable_name(child, source)),
11516    }
11517}
11518
11519/// Recover a C field whose name is wrapped in the function-like
11520/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
11521/// function declarator, while retaining its argument as a structured child.
11522/// The exact macro name and one-argument shape keep ordinary function-pointer
11523/// fields and unknown malformed declarators fail-closed.
11524#[derive(Clone, Copy)]
11525pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
11526    pub(crate) name: Node<'tree>,
11527    pub(crate) declarator: Node<'tree>,
11528}
11529
11530impl RecoveredFunctionLikeFieldDeclarator<'_> {
11531    pub(crate) fn pointer_depth(self) -> i32 {
11532        let mut depth = 0;
11533        let mut current = self.declarator;
11534        while current.kind() != "function_declarator" {
11535            if current.kind() == "pointer_declarator" {
11536                depth += 1;
11537            }
11538            current = current
11539                .child_by_field_name("declarator")
11540                .expect("recovered field wrapper has an inner declarator");
11541        }
11542        depth
11543    }
11544}
11545
11546/// Whether a class-scope field declaration is really a function-like macro
11547/// invocation whose replacement the parser never saw.
11548///
11549/// The invocation carries no terminator of its own, so tree-sitter invents a
11550/// zero-width `;` and reads the macro name as the member's type and the
11551/// argument list as a `parenthesized_declarator`. That shape is not a member:
11552/// a member terminates itself, and with a real terminator the grammar reads
11553/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
11554/// type can name the macro being invoked; a builtin type keeps whatever
11555/// recovery the ordinary declarator walk makes of it.
11556fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
11557    if node.kind() != "field_declaration" {
11558        return false;
11559    }
11560    let Some(type_node) = node.child_by_field_name("type") else {
11561        return false;
11562    };
11563    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
11564        return false;
11565    }
11566    let Some(declarator) = node.child_by_field_name("declarator") else {
11567        return false;
11568    };
11569    if declarator.kind() != "parenthesized_declarator" {
11570        return false;
11571    }
11572    let mut invented_terminator = false;
11573    for child in children_iter(node) {
11574        if child.kind() != ";" {
11575            continue;
11576        }
11577        if !child.is_missing() {
11578            return false;
11579        }
11580        invented_terminator = true;
11581    }
11582    invented_terminator
11583}
11584
11585pub(crate) fn recovered_function_like_field_declarator<'tree>(
11586    node: Node<'tree>,
11587    source: &str,
11588) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
11589    if node.kind() != "field_declaration" {
11590        return None;
11591    }
11592    let outer_declarator = node.child_by_field_name("declarator")?;
11593    let mut declarator = outer_declarator;
11594    while matches!(
11595        declarator.kind(),
11596        "pointer_declarator"
11597            | "reference_declarator"
11598            | "array_declarator"
11599            | "parenthesized_declarator"
11600    ) {
11601        declarator = declarator.child_by_field_name("declarator")?;
11602    }
11603    if declarator.kind() != "function_declarator" {
11604        return None;
11605    }
11606    let macro_name = declarator.child_by_field_name("declarator")?;
11607    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
11608    {
11609        return None;
11610    }
11611    let parameters = declarator.child_by_field_name("parameters")?;
11612    let mut cursor = parameters.walk();
11613    let mut arguments = parameters.named_children(&mut cursor);
11614    let parameter = arguments.next()?;
11615    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
11616        return None;
11617    }
11618    let name = parameter.child_by_field_name("type").filter(|argument| {
11619        matches!(
11620            argument.kind(),
11621            "identifier" | "field_identifier" | "type_identifier"
11622        )
11623    })?;
11624    Some(RecoveredFunctionLikeFieldDeclarator {
11625        name,
11626        declarator: outer_declarator,
11627    })
11628}
11629
11630fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
11631    let count = node.named_child_count();
11632    if count == 0 {
11633        None
11634    } else {
11635        node.named_child(count - 1)
11636    }
11637}
11638
11639fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
11640    let name_node = node.child_by_field_name("name")?;
11641    let name = normalize_cpp_whitespace(node_text(name_node, source));
11642    (!name.is_empty()).then_some(name)
11643}
11644
11645fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11646    if node.kind() != "declaration" {
11647        return Vec::new();
11648    }
11649    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
11650        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
11651    }) else {
11652        return Vec::new();
11653    };
11654    let Some(declarator) = node.child_by_field_name("declarator") else {
11655        return Vec::new();
11656    };
11657    if node_text(keyword, source) == "using"
11658        && (declarator.kind() != "init_declarator"
11659            || declarator.child_by_field_name("value").is_none())
11660    {
11661        return Vec::new();
11662    }
11663    if node_text(keyword, source) == "typedef"
11664        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
11665    {
11666        return vec![alias_name];
11667    }
11668    extract_typedef_declarator_name(declarator, source)
11669        .into_iter()
11670        .collect()
11671}
11672
11673/// The RHS retained by the same malformed alias shapes declaration admission
11674/// accepts. The alias name proves which declarator owns this source evidence.
11675pub(crate) fn recovered_alias_type_node<'tree>(
11676    node: Node<'tree>,
11677    alias_name: &str,
11678    source: &str,
11679) -> Option<Node<'tree>> {
11680    if node.kind() != "declaration" {
11681        return None;
11682    }
11683    let declarator = node.child_by_field_name("declarator")?;
11684    let admitted = recovered_type_alias_names(node, source)
11685        .iter()
11686        .any(|name| name == alias_name)
11687        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
11688    if !admitted {
11689        return None;
11690    }
11691    if declarator.kind() == "init_declarator" {
11692        return declarator.child_by_field_name("value");
11693    }
11694    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
11695        .then_some(declarator)
11696}
11697
11698fn recovered_typedef_error_alias_name(
11699    declaration: Node<'_>,
11700    declarator: Node<'_>,
11701    source: &str,
11702) -> Option<String> {
11703    // An export macro between `class` and its name can make tree-sitter parse
11704    // the recovered class body as a function body. In that shape,
11705    //
11706    //     typedef spi::Filter BASE_CLASS;
11707    //
11708    // becomes a declaration whose `declarator` is the underlying qualified
11709    // type (`spi::Filter`) and whose actual alias name is displaced into the
11710    // following ERROR node. Do not publish the terminal underlying type
11711    // (`Filter`) as a false class-owned alias.
11712    if declarator.kind() != "qualified_identifier" {
11713        return None;
11714    }
11715    let mut cursor = declaration.walk();
11716    let mut errors = declaration
11717        .named_children(&mut cursor)
11718        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
11719    let error = errors.next()?;
11720    if errors.next().is_some() || error.named_child_count() != 1 {
11721        return None;
11722    }
11723    let name = error.named_child(0)?;
11724    if !matches!(
11725        name.kind(),
11726        "identifier" | "field_identifier" | "type_identifier"
11727    ) {
11728        return None;
11729    }
11730    let name = normalize_cpp_whitespace(node_text(name, source));
11731    (!name.is_empty()).then_some(name)
11732}
11733
11734fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11735    // A function-like token in the type position can make tree-sitter expose
11736    // its argument as a parenthesized declarator. Do not publish that argument
11737    // as an alias. The macro-specific recovery below handles the proven shape.
11738    if fragmented_parenthesized_typedef_type(node).is_some() {
11739        return Vec::new();
11740    }
11741    let has_function_like_macro_type = node
11742        .child_by_field_name("type")
11743        .filter(|type_node| type_node.kind() == "type_identifier")
11744        .is_some_and(|type_node| {
11745            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
11746        });
11747    let mut names = Vec::new();
11748    let mut cursor = node.walk();
11749    for declarator in node.children_by_field_name("declarator", &mut cursor) {
11750        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
11751            continue;
11752        }
11753        if let Some(name) = extract_typedef_declarator_name(declarator, source)
11754            && !names.contains(&name)
11755        {
11756            names.push(name);
11757        }
11758    }
11759    names
11760}
11761
11762struct RecoveredMacroTypedefAlias<'tree> {
11763    name: String,
11764    end_node: Node<'tree>,
11765}
11766
11767/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
11768/// into an identifier expression statement. The uppercase macro token, missing
11769/// typedef terminator, and complete sibling terminator prove this exact shape.
11770fn recovered_macro_typedef_alias<'tree>(
11771    node: Node<'tree>,
11772    source: &str,
11773) -> Option<RecoveredMacroTypedefAlias<'tree>> {
11774    let type_node = fragmented_parenthesized_typedef_type(node)?;
11775    if type_node.kind() != "type_identifier"
11776        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
11777    {
11778        return None;
11779    }
11780
11781    let end_node = node.next_named_sibling()?;
11782    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
11783        return None;
11784    }
11785    let name_node = end_node.named_child(0)?;
11786    if name_node.kind() != "identifier" {
11787        return None;
11788    }
11789    let has_terminator = (0..end_node.child_count()).any(|index| {
11790        end_node
11791            .child(index)
11792            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
11793    });
11794    if !has_terminator {
11795        return None;
11796    }
11797    let name = normalize_cpp_whitespace(node_text(name_node, source));
11798    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
11799}
11800
11801fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
11802    if node.kind() != "type_definition" {
11803        return None;
11804    }
11805    let mut declarator_cursor = node.walk();
11806    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
11807    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
11808        return None;
11809    }
11810    let has_missing_terminator = (0..node.child_count()).any(|index| {
11811        node.child(index)
11812            .is_some_and(|child| child.kind() == ";" && child.is_missing())
11813    });
11814    if !has_missing_terminator {
11815        return None;
11816    }
11817    node.child_by_field_name("type")
11818}
11819
11820fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11821    match node.kind() {
11822        "identifier" | "field_identifier" | "type_identifier" => {
11823            let name = normalize_cpp_whitespace(node_text(node, source));
11824            (!name.is_empty()).then_some(name)
11825        }
11826        "qualified_identifier" => node
11827            .child_by_field_name("name")
11828            .and_then(|name| extract_typedef_declarator_name(name, source)),
11829        _ => node
11830            .child_by_field_name("declarator")
11831            .or_else(|| node.child_by_field_name("name"))
11832            .or_else(|| last_named_child(node))
11833            .and_then(|child| extract_typedef_declarator_name(child, source)),
11834    }
11835}
11836
11837fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
11838    let name = node
11839        .child_by_field_name("name")
11840        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
11841        .or_else(|| {
11842            let mut cursor = node.walk();
11843            node.named_children(&mut cursor)
11844                .find(|child| {
11845                    matches!(
11846                        child.kind(),
11847                        "identifier" | "field_identifier" | "type_identifier"
11848                    )
11849                })
11850                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
11851        })?;
11852    (!name.is_empty()).then_some(name)
11853}
11854
11855fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
11856    left.id() == right.id()
11857}
11858
11859fn render_cpp_type_signature(
11860    node: Node<'_>,
11861    source: &str,
11862    template_signature: Option<&str>,
11863) -> String {
11864    let text = normalize_cpp_whitespace(node_text(node, source));
11865    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
11866    let rendered = if head.ends_with(';') {
11867        head.to_string()
11868    } else {
11869        format!("{head} {{")
11870    };
11871    if let Some(template_signature) = template_signature {
11872        format!("template {template_signature} {rendered}")
11873    } else {
11874        rendered
11875    }
11876}
11877
11878fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
11879    if let Some(recovered) = recovered_pyobject_head_field(node, source)
11880        && recovered.declarator == declarator
11881    {
11882        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
11883        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
11884        return format!("{type_text} {declarator};");
11885    }
11886    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
11887        && recovered.name == declarator
11888    {
11889        let type_text = node
11890            .child_by_field_name("type")
11891            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
11892            .unwrap_or_default();
11893        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
11894        let mut prefix = String::new();
11895        let mut suffix = String::new();
11896        let mut current = recovered.declarator;
11897        while current.kind() != "function_declarator" {
11898            match current.kind() {
11899                "pointer_declarator" => prefix.push('*'),
11900                "reference_declarator" => prefix.push('&'),
11901                "array_declarator" => {
11902                    let size = current
11903                        .child_by_field_name("size")
11904                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
11905                        .unwrap_or_default();
11906                    suffix.push('[');
11907                    suffix.push_str(&size);
11908                    suffix.push(']');
11909                }
11910                "parenthesized_declarator" => {}
11911                _ => unreachable!("validated recovered field declarator wrapper"),
11912            }
11913            current = current
11914                .child_by_field_name("declarator")
11915                .expect("recovered field wrapper has an inner declarator");
11916        }
11917        let separator = if prefix.is_empty() { "" } else { " " };
11918        return format!("{type_text} {prefix}{separator}{name}{suffix};");
11919    }
11920    if let Some(signature) =
11921        render_recovered_macro_qualified_field_signature(node, declarator, source)
11922    {
11923        return signature;
11924    }
11925    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
11926    let prefix = cpp_declaration_prefix(node, source);
11927    let name = extract_variable_name(declarator, source).unwrap_or_default();
11928    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
11929    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
11930        || (prefix.ends_with('&') && raw_suffix == "&")
11931    {
11932        String::new()
11933    } else {
11934        raw_suffix
11935    };
11936
11937    let mut rendered = if suffix.is_empty() {
11938        format!("{prefix} {name}")
11939    } else if suffix.starts_with('*') || suffix.starts_with('&') {
11940        format!("{prefix}{suffix} {name}")
11941    } else if suffix.starts_with('[') || suffix.starts_with('(') {
11942        format!("{prefix} {name}{suffix}")
11943    } else {
11944        format!("{prefix} {suffix}{name}")
11945    };
11946    rendered = collapse_cpp_whitespace(&rendered);
11947
11948    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
11949        format!("{rendered} = {initializer};")
11950    } else if declaration_text.ends_with(';') {
11951        format!("{rendered};")
11952    } else {
11953        rendered
11954    }
11955}
11956
11957fn render_recovered_macro_qualified_field_signature(
11958    node: Node<'_>,
11959    declarator: Node<'_>,
11960    source: &str,
11961) -> Option<String> {
11962    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
11963    if !recovered
11964        .iter()
11965        .any(|candidate| same_node(*candidate, declarator))
11966    {
11967        return None;
11968    }
11969    let pseudo_declarator = node.child_by_field_name("declarator")?;
11970    let mut cursor = node.walk();
11971    let clause = node
11972        .named_children(&mut cursor)
11973        .find(|child| child.kind() == "bitfield_clause")?;
11974    let mut cursor = clause.walk();
11975    let error = clause
11976        .named_children(&mut cursor)
11977        .find(|child| child.kind() == "ERROR")?;
11978    let qualified_type =
11979        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
11980    let prefix = cpp_declaration_prefix(node, source);
11981    let name = extract_variable_name(declarator, source)?;
11982    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
11983    let mut rendered = if suffix.is_empty() {
11984        format!("{prefix} {qualified_type} {name}")
11985    } else {
11986        format!("{prefix} {qualified_type} {suffix} {name}")
11987    };
11988    rendered = collapse_cpp_whitespace(&rendered);
11989
11990    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
11991        Some(format!(
11992            "{rendered} = {};",
11993            normalize_cpp_whitespace(node_text(initializer, source))
11994        ))
11995    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
11996        Some(format!("{rendered} = {initializer};"))
11997    } else {
11998        Some(format!("{rendered};"))
11999    }
12000}
12001
12002fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
12003    match node.kind() {
12004        "pointer_expression" => {
12005            let operator = node
12006                .child_by_field_name("operator")
12007                .or_else(|| node.child(0))
12008                .map(|operator| node_text(operator, source))
12009                .unwrap_or("*");
12010            let argument = node
12011                .child_by_field_name("argument")
12012                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12013                .unwrap_or_default();
12014            format!("{operator}{argument}")
12015        }
12016        "unary_expression" => {
12017            let operator = node
12018                .child_by_field_name("operator")
12019                .or_else(|| node.child(0))
12020                .map(|operator| node_text(operator, source))
12021                .unwrap_or_default();
12022            let argument = node
12023                .child_by_field_name("argument")
12024                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12025                .unwrap_or_default();
12026            format!("{operator}{argument}")
12027        }
12028        "identifier" | "field_identifier" => String::new(),
12029        _ => cpp_declarator_suffix_without_name(node, source),
12030    }
12031}
12032
12033fn recovered_macro_qualified_field_initializer<'tree>(
12034    clause: Node<'tree>,
12035    declarator: Node<'tree>,
12036) -> Option<Node<'tree>> {
12037    let mut stack = vec![clause];
12038    while let Some(current) = stack.pop() {
12039        if current.kind() == "assignment_expression"
12040            && current
12041                .child_by_field_name("left")
12042                .is_some_and(|left| same_node(left, declarator))
12043        {
12044            return current.child_by_field_name("right");
12045        }
12046        let mut cursor = current.walk();
12047        stack.extend(current.named_children(&mut cursor));
12048    }
12049    None
12050}
12051
12052fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
12053    let text = node_text(node, source);
12054    let mut cursor = node.walk();
12055    let first_declarator = node.named_children(&mut cursor).find(|child| {
12056        matches!(
12057            child.kind(),
12058            "init_declarator"
12059                | "identifier"
12060                | "field_identifier"
12061                | "pointer_declarator"
12062                | "reference_declarator"
12063                | "array_declarator"
12064                | "function_declarator"
12065        )
12066    });
12067    let prefix = if let Some(first_declarator) = first_declarator {
12068        let end = first_declarator
12069            .start_byte()
12070            .saturating_sub(node.start_byte());
12071        let mut prefix = text.get(..end).unwrap_or(text).to_string();
12072        let declarator_suffix = match first_declarator.kind() {
12073            "init_declarator" => first_declarator
12074                .child_by_field_name("declarator")
12075                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
12076                .unwrap_or_default(),
12077            _ => cpp_declarator_suffix_without_name(first_declarator, source),
12078        };
12079        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
12080            prefix.push_str(&declarator_suffix);
12081        }
12082        return collapse_cpp_whitespace(&prefix)
12083            .trim_end_matches(',')
12084            .trim_end_matches(';')
12085            .trim()
12086            .to_string();
12087    } else {
12088        text
12089    };
12090    collapse_cpp_whitespace(prefix)
12091        .trim_end_matches(',')
12092        .trim_end_matches(';')
12093        .trim()
12094        .to_string()
12095}
12096
12097fn cpp_preserved_initializer(
12098    declaration_node: Node<'_>,
12099    declarator: Node<'_>,
12100    source: &str,
12101) -> Option<String> {
12102    let name = extract_variable_name(declarator, source)?;
12103    let mut cursor = declaration_node.walk();
12104    for child in declaration_node.named_children(&mut cursor) {
12105        if child.kind() != "init_declarator" {
12106            continue;
12107        }
12108        let Some(inner) = child.child_by_field_name("declarator") else {
12109            continue;
12110        };
12111        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
12112            continue;
12113        }
12114        let value = child.child_by_field_name("value")?;
12115        let kind = value.kind();
12116        if matches!(
12117            kind,
12118            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
12119        ) {
12120            return Some(normalize_cpp_whitespace(node_text(value, source)));
12121        }
12122        break;
12123    }
12124    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
12125    let pattern = format!(
12126        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
12127        regex::escape(&name)
12128    );
12129    Regex::new(&pattern)
12130        .ok()
12131        .and_then(|regex| regex.captures(&declaration_text))
12132        .and_then(|captures| captures.get(1))
12133        .map(|value| value.as_str().to_string())
12134}
12135
12136fn render_cpp_function_display_signature_from_node<'tree>(
12137    node: Node<'tree>,
12138    source: &str,
12139    template_signature: Option<&str>,
12140    has_body: bool,
12141    ancestry: &ParentIndex<'tree>,
12142) -> String {
12143    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
12144    let parent_text = node_text(root, source);
12145    let body_local_start = root
12146        .child_by_field_name("body")
12147        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
12148        .unwrap_or(parent_text.len());
12149    let display = parent_text
12150        .get(..body_local_start)
12151        .unwrap_or(parent_text)
12152        .trim()
12153        .trim();
12154    let display = if let Some(template_signature) = template_signature {
12155        if display.starts_with("template ") {
12156            display.to_string()
12157        } else {
12158            format!("template {template_signature} {display}")
12159        }
12160    } else {
12161        display.to_string()
12162    };
12163    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
12164    if has_body {
12165        format!("{display} {{...}}")
12166    } else {
12167        format!("{display};")
12168    }
12169}
12170
12171fn cpp_template_signature(
12172    template_node: Node<'_>,
12173    declaration_child: Node<'_>,
12174    source: &str,
12175) -> Option<String> {
12176    let text = source
12177        .get(template_node.start_byte()..declaration_child.start_byte())
12178        .unwrap_or("");
12179    let text = normalize_cpp_whitespace(text);
12180    let start = text.find('<')?;
12181    let end = text.rfind('>')?;
12182    if end < start {
12183        return None;
12184    }
12185    Some(text[start..=end].to_string())
12186}
12187
12188struct RecoveredFragmentedPartialSpecialization<'tree> {
12189    declaration_node: Node<'tree>,
12190    name: String,
12191    range: Range,
12192    prefix_members: Vec<Node<'tree>>,
12193    member_siblings: Vec<Node<'tree>>,
12194    following_declarations: Vec<Node<'tree>>,
12195}
12196
12197struct RecoveredFragmentedPreprocessorClass<'tree> {
12198    declaration_node: Node<'tree>,
12199    class_node: Node<'tree>,
12200    body: Node<'tree>,
12201    name: String,
12202    range: Range,
12203    tail_members: Vec<Node<'tree>>,
12204    member_siblings: Vec<Node<'tree>>,
12205}
12206
12207/// Recover a class whose preprocessor-fragmented parse closes at an early
12208/// member body and publishes the remaining in-class declarations as siblings
12209/// of the surrounding alternative. Primary classes are admitted only when an
12210/// earlier branch contains the matching bodyless declaration and the class
12211/// node retains the displaced `#endif`. Partial specializations instead carry
12212/// their identity structurally in the `template_type` name and template
12213/// metadata. Retain the original AST nodes and re-own only the siblings through
12214/// the displaced structural `};` terminator.
12215fn recover_fragmented_preprocessor_class<'tree>(
12216    template_node: Node<'tree>,
12217    source: &str,
12218    ancestry: &ParentIndex<'tree>,
12219) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
12220    let alternative = ancestry.parent(template_node)?;
12221    if alternative.kind() != "preproc_else" {
12222        return None;
12223    }
12224    let conditional = alternative.parent()?;
12225    if conditional.kind() != "preproc_if" {
12226        return None;
12227    }
12228    let declaration_node = template_node
12229        .named_children(&mut template_node.walk())
12230        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
12231    let class_node = declaration_node
12232        .named_children(&mut declaration_node.walk())
12233        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
12234    let body = cpp_body_node(class_node)?;
12235    if class_node.end_byte() >= declaration_node.end_byte() {
12236        return None;
12237    }
12238    let name = class_like_name(class_node, source, ancestry)?;
12239    let is_partial_specialization = class_node
12240        .child_by_field_name("name")
12241        .is_some_and(|class_name| class_name.kind() == "template_type");
12242    if is_partial_specialization {
12243        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
12244        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
12245            return None;
12246        }
12247    } else {
12248        if !class_has_displaced_preprocessor_terminator(class_node) {
12249            return None;
12250        }
12251        let matching_other_branch = conditional
12252            .named_children(&mut conditional.walk())
12253            .take_while(|child| !same_node(*child, alternative))
12254            .filter(|child| child.kind() == "template_declaration")
12255            .filter_map(first_class_like_child)
12256            .any(|candidate| {
12257                cpp_body_node(candidate).is_none()
12258                    && class_like_name(candidate, source, ancestry).as_deref()
12259                        == Some(name.as_str())
12260            });
12261        if !matching_other_branch {
12262            return None;
12263        }
12264    }
12265
12266    let mut tail_members = Vec::new();
12267    let mut saw_class = false;
12268    let mut declaration_cursor = declaration_node.walk();
12269    for child in declaration_node.named_children(&mut declaration_cursor) {
12270        if same_node(child, class_node) {
12271            saw_class = true;
12272        } else if saw_class {
12273            tail_members.push(child);
12274        }
12275    }
12276
12277    let mut member_siblings = Vec::new();
12278    let mut saw_template = false;
12279    let mut terminator = None;
12280    for index in 0..alternative.child_count() {
12281        let Some(child) = alternative.child(index) else {
12282            continue;
12283        };
12284        if same_node(child, template_node) {
12285            saw_template = true;
12286            continue;
12287        }
12288        if !saw_template {
12289            continue;
12290        }
12291        if displaced_fragmented_class_terminator(alternative, index) {
12292            terminator = alternative.child(index + 1);
12293            break;
12294        }
12295        if child.is_named() {
12296            member_siblings.push(child);
12297        }
12298    }
12299    let terminator = terminator?;
12300    Some(RecoveredFragmentedPreprocessorClass {
12301        declaration_node,
12302        class_node,
12303        body,
12304        name,
12305        range: Range {
12306            start_byte: class_node.start_byte(),
12307            end_byte: terminator.end_byte(),
12308            start_line: class_node.start_position().row + 1,
12309            end_line: terminator.end_position().row + 1,
12310        },
12311        tail_members,
12312        member_siblings,
12313    })
12314}
12315
12316fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
12317    (0..class_node.child_count()).any(|index| {
12318        class_node.child(index).is_some_and(|child| {
12319            child.kind() == "ERROR"
12320                && (0..child.child_count()).any(|error_index| {
12321                    child
12322                        .child(error_index)
12323                        .is_some_and(|token| token.kind() == "#endif")
12324                })
12325        })
12326    })
12327}
12328
12329/// The real `#endif` that tree-sitter consumed inside an error subtree.
12330///
12331/// A preprocessor directive inside a malformed array bound can cause later
12332/// declarations to remain children of the conditional. The non-missing token
12333/// still gives the exact structured boundary. Ignore nested conditionals and
12334/// select the last unpaired error-owned token. Tree-sitter can pair a later
12335/// outer `#endif` with this conditional, so the direct terminator is not
12336/// necessarily missing.
12337pub fn cpp_displaced_preprocessor_terminator<'tree>(
12338    conditional: Node<'tree>,
12339) -> Option<Node<'tree>> {
12340    if !conditional.has_error() {
12341        return None;
12342    }
12343    let has_concrete_direct_terminator = conditional
12344        .child_count()
12345        .checked_sub(1)
12346        .and_then(|index| conditional.child(index))
12347        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
12348    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
12349        // A structured alternative proves that the direct `#endif` closes
12350        // this family. An error-owned terminator inside either branch belongs
12351        // to a damaged nested conditional, not to this one.
12352        return None;
12353    }
12354    // Pair concrete directive tokens while walking the recovered subtree. An
12355    // inner conditional may be represented only by its `#ifndef`/`#endif`
12356    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
12357    // sufficient: an inner `#endif` can otherwise be mistaken for this
12358    // conditional's displaced terminator.
12359    let mut displaced = None;
12360    let mut conditional_depth = 0usize;
12361    let mut stack = children_iter(conditional)
12362        .map(|child| (child, false))
12363        .collect::<Vec<_>>();
12364    stack.reverse();
12365    while let Some((node, inside_error)) = stack.pop() {
12366        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
12367            continue;
12368        }
12369        if node != conditional
12370            && matches!(
12371                node.kind(),
12372                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
12373            )
12374        {
12375            continue;
12376        }
12377        let inside_error = inside_error || node.kind() == "ERROR";
12378        match node.kind() {
12379            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
12380                conditional_depth += 1;
12381            }
12382            "#endif" if !node.is_missing() => {
12383                if conditional_depth == 0
12384                    && inside_error
12385                    && displaced
12386                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
12387                {
12388                    displaced = Some(node);
12389                }
12390                conditional_depth = conditional_depth.saturating_sub(1);
12391            }
12392            _ => {}
12393        }
12394        let first_pushed = stack.len();
12395        stack.extend(children_iter(node).map(|child| (child, inside_error)));
12396        stack[first_pushed..].reverse();
12397    }
12398    displaced
12399}
12400
12401/// The effective end of a conditional whose real terminator tree-sitter
12402/// displaced into declaration recovery.
12403///
12404/// Most damaged conditionals retain a concrete `#endif` token below an
12405/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
12406/// boundary. A preprocessor family that selects the middle of a declaration
12407/// can lose the directive tokens entirely. In that shape tree-sitter leaves
12408/// the declaration's `typedef` token as the sole child of the immediately
12409/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
12410/// declarator name inside the conditional's first declaration. The declaration
12411/// end is then the smallest structured boundary that contains the whole split
12412/// declaration.
12413#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12414pub struct CppDisplacedPreprocessorBoundary {
12415    pub end_byte: usize,
12416    pub end_line: usize,
12417}
12418
12419pub fn cpp_displaced_preprocessor_boundary(
12420    conditional: Node<'_>,
12421) -> Option<CppDisplacedPreprocessorBoundary> {
12422    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
12423        return Some(CppDisplacedPreprocessorBoundary {
12424            end_byte: terminator.end_byte(),
12425            end_line: terminator.end_position().row + 1,
12426        });
12427    }
12428    if let Some(declaration) = displaced_split_declaration(conditional) {
12429        return Some(CppDisplacedPreprocessorBoundary {
12430            end_byte: declaration.end_byte(),
12431            end_line: declaration.end_position().row + 1,
12432        });
12433    }
12434    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
12435        return Some(CppDisplacedPreprocessorBoundary {
12436            end_byte: terminator.end_byte(),
12437            end_line: terminator.end_position().row + 1,
12438        });
12439    }
12440    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
12441        return Some(CppDisplacedPreprocessorBoundary {
12442            end_byte: terminator.end_byte(),
12443            end_line: terminator.end_position().row + 1,
12444        });
12445    }
12446    None
12447}
12448
12449/// Recover an outer terminator that tree-sitter assigned to a damaged nested
12450/// conditional. This occurs when a split construct such as `extern "C"`
12451/// consumes the nested `#endif` inside an error node: the nested conditional's
12452/// direct terminator is then the outer conditional's real terminator, while
12453/// the outer node ends with a missing token and absorbs later declarations.
12454fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12455    if !conditional.has_error()
12456        || conditional.child_by_field_name("alternative").is_some()
12457        || conditional
12458            .child(conditional.child_count().saturating_sub(1))
12459            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
12460    {
12461        return None;
12462    }
12463    let mut recovered = None;
12464    for nested in named_children_iter(conditional) {
12465        if !matches!(
12466            nested.kind(),
12467            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
12468        ) || nested.child_by_field_name("alternative").is_some()
12469        {
12470            continue;
12471        }
12472        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
12473            continue;
12474        };
12475        if direct.kind() != "#endif" || direct.is_missing() {
12476            continue;
12477        }
12478        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
12479            continue;
12480        };
12481        if displaced.end_byte() >= direct.start_byte() {
12482            continue;
12483        }
12484        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
12485            recovered = Some(direct);
12486        }
12487    }
12488    recovered
12489}
12490
12491fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12492    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
12493        return None;
12494    }
12495    let mut cursor = conditional.walk();
12496    let declarations = conditional
12497        .named_children(&mut cursor)
12498        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
12499        .collect::<Vec<_>>();
12500    let declaration = *declarations.first()?;
12501    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
12502        return None;
12503    }
12504    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
12505    let mut terminator = None;
12506    let mut stack = (0..declaration.child_count())
12507        .filter_map(|index| declaration.child(index))
12508        .filter(|child| child.start_byte() < declarator_start)
12509        .map(|child| (child, false))
12510        .collect::<Vec<_>>();
12511    while let Some((node, inside_error)) = stack.pop() {
12512        let inside_error = inside_error || node.kind() == "ERROR";
12513        if inside_error && node.kind() == "#endif" && !node.is_missing() {
12514            terminator = Some(node);
12515            continue;
12516        }
12517        for index in 0..node.child_count() {
12518            if let Some(child) = node.child(index)
12519                && child.start_byte() < declarator_start
12520            {
12521                stack.push((child, inside_error));
12522            }
12523        }
12524    }
12525    terminator
12526}
12527
12528fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12529    if !conditional.has_error()
12530        || conditional.child_by_field_name("alternative").is_some()
12531        || conditional
12532            .prev_named_sibling()
12533            .filter(|sibling| {
12534                sibling.kind() == "ERROR"
12535                    && sibling.child_count() == 1
12536                    && sibling
12537                        .child(0)
12538                        .is_some_and(|child| child.kind() == "typedef")
12539            })
12540            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
12541            .is_none()
12542    {
12543        return None;
12544    }
12545    let mut cursor = conditional.walk();
12546    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
12547    let declaration_index = children
12548        .iter()
12549        .position(|child| child.kind() == "declaration" && child.has_error())?;
12550    let declaration = children[declaration_index];
12551    if !children
12552        .iter()
12553        .skip(declaration_index + 1)
12554        .any(|child| child.end_byte() > declaration.end_byte())
12555    {
12556        return None;
12557    }
12558    let declarator = declaration.child_by_field_name("declarator")?;
12559    let mut error_end = None;
12560    let mut names = Vec::new();
12561    let mut stack = vec![declarator];
12562    while let Some(node) = stack.pop() {
12563        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
12564            error_end =
12565                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
12566            continue;
12567        }
12568        if matches!(node.kind(), "identifier" | "type_identifier") {
12569            names.push(node.start_byte());
12570        }
12571        push_named_children_reversed(node, &mut stack);
12572    }
12573    let error_end = error_end?;
12574    names
12575        .into_iter()
12576        .any(|start| start >= error_end)
12577        .then_some(declaration)
12578}
12579
12580fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
12581    let Some(error) = parent.child(error_index) else {
12582        return false;
12583    };
12584    if error.kind() != "ERROR"
12585        || error.child_count() != 1
12586        || error.child(0).is_none_or(|child| child.kind() != "}")
12587    {
12588        return false;
12589    }
12590    let Some(semicolon) = parent.child(error_index + 1) else {
12591        return false;
12592    };
12593    semicolon.kind() == "expression_statement"
12594        && semicolon.child_count() == 1
12595        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
12596}
12597
12598/// Locate the real end of a class-like declaration when a macro invocation
12599/// without a source semicolon absorbs the class's `};` into its parsed field.
12600/// The grammar then keeps following namespace declarations as later children
12601/// of the same field list. The direct ERROR-plus-semicolon pair proves the
12602/// boundary structurally; no source-text delimiter scan is needed.
12603fn displaced_macro_class_tail(
12604    declaration_node: Node<'_>,
12605    body: Node<'_>,
12606    source: &str,
12607) -> Option<DisplacedMacroClassTail> {
12608    if !matches!(
12609        declaration_node.kind(),
12610        "class_specifier" | "struct_specifier" | "union_specifier"
12611    ) || body.kind() != "field_declaration_list"
12612    {
12613        return None;
12614    }
12615
12616    let child_count = body.named_child_count();
12617    for index in 0..child_count {
12618        let child = body.named_child(index)?;
12619        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
12620            continue;
12621        };
12622        let split_index = index + 1;
12623        if split_index >= child_count {
12624            return None;
12625        }
12626        let mut cursor = body.walk();
12627        if !body
12628            .named_children(&mut cursor)
12629            .skip(split_index)
12630            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
12631        {
12632            return None;
12633        }
12634        return Some(DisplacedMacroClassTail {
12635            split_index,
12636            class_range: Range {
12637                start_byte: declaration_node.start_byte(),
12638                end_byte: terminator.end_byte(),
12639                start_line: declaration_node.start_position().row + 1,
12640                end_line: terminator.end_position().row + 1,
12641            },
12642        });
12643    }
12644    None
12645}
12646
12647fn displaced_macro_field_terminator<'tree>(
12648    field: Node<'tree>,
12649    source: &str,
12650) -> Option<Node<'tree>> {
12651    if field.kind() != "field_declaration" {
12652        return None;
12653    }
12654    let macro_type = field.child_by_field_name("type")?;
12655    if macro_type.kind() != "type_identifier"
12656        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12657        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
12658    {
12659        return None;
12660    }
12661    for index in 0..field.child_count() {
12662        let error = field.child(index)?;
12663        if error.kind() != "ERROR"
12664            || error.child_count() != 1
12665            || error.child(0).is_none_or(|child| child.kind() != "}")
12666        {
12667            continue;
12668        }
12669        let semicolon = field.child(index + 1)?;
12670        if semicolon.kind() == ";" {
12671            return Some(semicolon);
12672        }
12673    }
12674    None
12675}
12676
12677fn recover_fragmented_partial_specialization<'tree>(
12678    template_node: Node<'tree>,
12679    declaration_child: Node<'tree>,
12680    source: &str,
12681    ancestry: &ParentIndex<'tree>,
12682) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
12683    if declaration_child.kind() != "function_definition" {
12684        return None;
12685    }
12686    let class_node = declaration_child.child_by_field_name("type")?;
12687    if !matches!(
12688        class_node.kind(),
12689        "class_specifier" | "struct_specifier" | "union_specifier"
12690    ) || !class_node
12691        .child_by_field_name("name")
12692        .and_then(|name| direct_identifier_name(name, source))
12693        .is_some_and(|name| cpp_export_macro_token(&name))
12694    {
12695        return None;
12696    }
12697    let declarator = declaration_child.child_by_field_name("declarator")?;
12698    if declarator.kind() != "template_function" {
12699        return None;
12700    }
12701    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
12702    if metadata.specialization_arguments.is_empty() {
12703        return None;
12704    }
12705    let body = declaration_child.child_by_field_name("body")?;
12706    if body.kind() != "compound_statement" {
12707        return None;
12708    }
12709    let complete_prefix = body.named_child(0).filter(|first| {
12710        first.kind() == "labeled_statement"
12711            && first.has_error()
12712            && first
12713                .named_child(first.named_child_count().saturating_sub(1))
12714                .is_some_and(recovered_declaration_has_class_terminator)
12715    });
12716    let complete_body = complete_prefix.is_some();
12717    let mut prefix_members = Vec::new();
12718    if let Some(prefix) = complete_prefix {
12719        prefix_members.push(prefix);
12720    } else {
12721        let mut body_cursor = body.walk();
12722        for child in body.named_children(&mut body_cursor) {
12723            if !is_structurally_valid_fragmented_class_prefix_member(child) {
12724                break;
12725            }
12726            prefix_members.push(child);
12727        }
12728    }
12729    let containing_declarations = template_node.parent()?;
12730    if !matches!(
12731        containing_declarations.kind(),
12732        "declaration_list" | "compound_statement"
12733    ) {
12734        return None;
12735    }
12736    let mut member_siblings = Vec::new();
12737    let mut following_declarations = Vec::new();
12738    let terminator;
12739    if complete_body {
12740        terminator = complete_prefix?;
12741        let mut cursor = body.walk();
12742        let mut after_prefix = false;
12743        for child in body.named_children(&mut cursor) {
12744            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
12745                after_prefix = true;
12746            } else if after_prefix {
12747                following_declarations.push(child);
12748            }
12749        }
12750    } else {
12751        let mut found_template = false;
12752        let mut cursor = containing_declarations.walk();
12753        let mut class_terminator = None;
12754        for child in containing_declarations.children(&mut cursor) {
12755            if same_node(child, template_node) {
12756                found_template = true;
12757                continue;
12758            }
12759            if found_template && child.kind() == "}" {
12760                class_terminator = Some(child);
12761                break;
12762            }
12763            // A namespace can never be a class member: reaching one before the
12764            // terminator proves the class's true close was swallowed upstream
12765            // and this scan has crossed into the enclosing scope, so the
12766            // recovery cannot be bounded -- continuing re-owns the namespace
12767            // block (and its template specializations) as class members under
12768            // a re-appended package, desyncing the fq boundary (#2306).
12769            if found_template && child.kind() == "namespace_definition" {
12770                return None;
12771            }
12772            if found_template && child.is_named() {
12773                member_siblings.push(child);
12774            }
12775        }
12776        terminator = class_terminator?;
12777    }
12778    let name = format!(
12779        "{}<{}>",
12780        metadata.primary_name,
12781        metadata
12782            .specialization_arguments
12783            .iter()
12784            .map(|argument| argument.text.as_str())
12785            .collect::<Vec<_>>()
12786            .join(", ")
12787    );
12788    Some(RecoveredFragmentedPartialSpecialization {
12789        declaration_node: declaration_child,
12790        name,
12791        range: Range {
12792            start_byte: declaration_child.start_byte(),
12793            end_byte: terminator.end_byte(),
12794            start_line: declaration_child.start_position().row + 1,
12795            end_line: terminator.end_position().row + 1,
12796        },
12797        prefix_members,
12798        member_siblings,
12799        following_declarations,
12800    })
12801}
12802
12803/// Whether `node` is the function-shaped parser wrapper whose body was
12804/// recovered as a fragmented partial-specialization class body.
12805///
12806/// Consumers that walk lexical function scopes must use the same complete
12807/// structural proof as declaration extraction. Otherwise member callables in
12808/// the recovered body are mistaken for function-local declarations.
12809pub fn is_recovered_fragmented_partial_specialization_container(
12810    node: Node<'_>,
12811    source: &str,
12812) -> bool {
12813    let Some(template) = node
12814        .parent()
12815        .filter(|parent| parent.kind() == "template_declaration")
12816    else {
12817        return false;
12818    };
12819    let mut root = template;
12820    while let Some(parent) = root.parent() {
12821        root = parent;
12822    }
12823    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
12824        .is_some()
12825}
12826
12827fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
12828    if declaration.kind() != "declaration" {
12829        return false;
12830    }
12831    // With an export macro between `class` and its name, tree-sitter folds a
12832    // complete class body into a function-shaped declaration. The class's own
12833    // `};` remains structurally identifiable as a direct ERROR child holding
12834    // `}`, immediately followed by the declaration's direct `;` child.
12835    (0..declaration.child_count().saturating_sub(1)).any(|index| {
12836        let Some(error) = declaration.child(index) else {
12837            return false;
12838        };
12839        error.kind() == "ERROR"
12840            && error.child_count() == 1
12841            && error.child(0).is_some_and(|child| child.kind() == "}")
12842            && declaration
12843                .child(index + 1)
12844                .is_some_and(|child| child.kind() == ";")
12845    })
12846}
12847
12848fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
12849    if node.has_error() {
12850        return false;
12851    }
12852    match node.kind() {
12853        "declaration"
12854        | "field_declaration"
12855        | "alias_declaration"
12856        | "type_definition"
12857        | "static_assert_declaration" => true,
12858        "labeled_statement" => node
12859            .named_child(node.named_child_count().saturating_sub(1))
12860            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
12861        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
12862            matches!(
12863                child.kind(),
12864                "declaration"
12865                    | "field_declaration"
12866                    | "alias_declaration"
12867                    | "type_definition"
12868                    | "function_definition"
12869            )
12870        }),
12871        _ => false,
12872    }
12873}
12874
12875fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
12876    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
12877        .then(|| node.child_by_field_name("declarator"))
12878        .flatten()
12879        .and_then(|declarator| extract_variable_name(declarator, source))
12880}
12881
12882fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
12883    while let Some(parent) = node.parent() {
12884        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
12885            return true;
12886        }
12887        node = parent;
12888    }
12889    false
12890}
12891
12892fn cpp_template_metadata<'tree>(
12893    template_node: Node<'tree>,
12894    declaration_child: Node<'tree>,
12895    source: &str,
12896    ancestry: &ParentIndex<'tree>,
12897) -> Option<CppTemplateMetadata> {
12898    let parameters_node = template_node.child_by_field_name("parameters")?;
12899    let name_node = cpp_templated_class_name_node(declaration_child)?;
12900    let primary_node = match name_node.kind() {
12901        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
12902        _ => name_node,
12903    };
12904    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
12905    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
12906        return None;
12907    }
12908
12909    let mut parameter_nodes = Vec::new();
12910    let mut parameter_names = Vec::new();
12911    let mut cursor = parameters_node.walk();
12912    for parameter in parameters_node.named_children(&mut cursor) {
12913        if !matches!(
12914            parameter.kind(),
12915            "type_parameter_declaration"
12916                | "optional_type_parameter_declaration"
12917                | "variadic_type_parameter_declaration"
12918                | "template_template_parameter_declaration"
12919                | "parameter_declaration"
12920                | "optional_parameter_declaration"
12921                | "variadic_parameter_declaration"
12922        ) {
12923            continue;
12924        }
12925        let index = parameter_nodes.len();
12926        // An unnamed parameter still contributes template arity and kind. Use
12927        // an impossible C++ identifier so positional reconciliation can bind
12928        // it without making source expressions refer to a name that was not
12929        // written.
12930        let name = cpp_template_parameter_name(parameter, source)
12931            .unwrap_or_else(|| format!("<anonymous:{index}>"));
12932        parameter_names.push(name);
12933        parameter_nodes.push(parameter);
12934    }
12935    let parameters = parameter_nodes
12936        .into_iter()
12937        .zip(parameter_names.iter().cloned())
12938        .map(|(parameter, name)| CppTemplateParameterMetadata {
12939            name,
12940            kind: cpp_template_parameter_kind(parameter),
12941            variadic: matches!(
12942                parameter.kind(),
12943                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
12944            ),
12945            default: cpp_template_parameter_default_expression(
12946                parameter,
12947                source,
12948                &parameter_names,
12949                ancestry,
12950            ),
12951        })
12952        .collect();
12953    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
12954        Vec::new()
12955    } else {
12956        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
12957            .unwrap_or_default()
12958    };
12959    let alias_target = (declaration_child.kind() == "alias_declaration")
12960        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
12961        .flatten();
12962    Some(CppTemplateMetadata {
12963        primary_name,
12964        primary_fq_name: String::new(),
12965        parameters,
12966        specialization_arguments,
12967        alias_target,
12968    })
12969}
12970
12971fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
12972    match node.kind() {
12973        "class_specifier" | "struct_specifier" | "union_specifier" => {
12974            node.child_by_field_name("name")
12975        }
12976        "function_definition" => {
12977            let declarator = node.child_by_field_name("declarator")?;
12978            if matches!(declarator.kind(), "identifier" | "template_function") {
12979                Some(declarator)
12980            } else {
12981                None
12982            }
12983        }
12984        "alias_declaration" => node.child_by_field_name("name"),
12985        _ => None,
12986    }
12987}
12988
12989fn cpp_template_alias_target<'tree>(
12990    alias: Node<'tree>,
12991    source: &str,
12992    parameter_names: &[String],
12993    ancestry: &ParentIndex<'tree>,
12994) -> Option<CppTemplateAliasTargetMetadata> {
12995    let mut type_node = alias.child_by_field_name("type")?;
12996    while type_node.kind() == "type_descriptor" {
12997        type_node = type_node.child_by_field_name("type")?;
12998    }
12999    let global = type_node.child_by_field_name("scope").is_none()
13000        && type_node.child(0).is_some_and(|child| child.kind() == "::");
13001    let mut components = Vec::new();
13002    cpp_template_target_components(type_node, source, &mut components)?;
13003    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
13004    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
13005        components,
13006        global,
13007        arguments,
13008    })
13009}
13010
13011fn cpp_template_target_components(
13012    node: Node<'_>,
13013    source: &str,
13014    out: &mut Vec<String>,
13015) -> Option<()> {
13016    match node.kind() {
13017        "identifier" | "namespace_identifier" | "type_identifier" => {
13018            out.push(node_text(node, source).to_string());
13019            Some(())
13020        }
13021        "template_type" => {
13022            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13023        }
13024        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13025            if let Some(scope) = node.child_by_field_name("scope") {
13026                cpp_template_target_components(scope, source, out)?;
13027            }
13028            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13029        }
13030        _ => None,
13031    }
13032}
13033
13034fn cpp_template_argument_expressions<'tree>(
13035    mut node: Node<'tree>,
13036    source: &str,
13037    parameter_names: &[String],
13038    ancestry: &ParentIndex<'tree>,
13039) -> Option<Vec<CppTemplateExpression>> {
13040    loop {
13041        match node.kind() {
13042            "template_type" | "template_function" => {
13043                let arguments = node.child_by_field_name("arguments")?;
13044                let mut cursor = arguments.walk();
13045                return Some(
13046                    arguments
13047                        .named_children(&mut cursor)
13048                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
13049                        .map(|argument| {
13050                            cpp_template_expression(argument, source, parameter_names, ancestry)
13051                        })
13052                        .collect(),
13053                );
13054            }
13055            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
13056                node = node
13057                    .child_by_field_name("name")
13058                    .or_else(|| node.child_by_field_name("type"))?;
13059            }
13060            _ => return None,
13061        }
13062    }
13063}
13064
13065fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
13066    let candidate = node
13067        .child_by_field_name("name")
13068        .or_else(|| node.child_by_field_name("declarator"))
13069        .or_else(|| {
13070            let mut cursor = node.walk();
13071            node.named_children(&mut cursor).find(|child| {
13072                matches!(
13073                    child.kind(),
13074                    "identifier" | "type_identifier" | "field_identifier"
13075                )
13076            })
13077        })?;
13078    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
13079    (!name.is_empty()).then_some(name)
13080}
13081
13082fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
13083    match node.kind() {
13084        "type_parameter_declaration"
13085        | "optional_type_parameter_declaration"
13086        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
13087        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
13088        _ => CppTemplateParameterKind::Value,
13089    }
13090}
13091
13092fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
13093    node.child_by_field_name("default_type")
13094        .or_else(|| node.child_by_field_name("default_value"))
13095}
13096
13097fn cpp_template_parameter_default_expression<'tree>(
13098    parameter: Node<'tree>,
13099    source: &str,
13100    parameter_names: &[String],
13101    ancestry: &ParentIndex<'tree>,
13102) -> Option<CppTemplateExpression> {
13103    let default = cpp_template_parameter_default(parameter)?;
13104    let base = cpp_template_expression(default, source, parameter_names, ancestry);
13105    let Some(pointer_error) = parameter.next_named_sibling() else {
13106        return Some(base);
13107    };
13108    let Some(pointer_declarator) =
13109        recovered_abstract_pointer_declarator_term(pointer_error, source)
13110    else {
13111        return Some(base);
13112    };
13113    Some(CppTemplateExpression {
13114        text: format!(
13115            "{}{}",
13116            base.text,
13117            normalize_cpp_whitespace(node_text(pointer_error, source))
13118        ),
13119        term: CppTemplateTerm::Node {
13120            kind: "type_descriptor".to_string(),
13121            children: vec![base.term, pointer_declarator],
13122        },
13123    })
13124}
13125
13126fn recovered_abstract_pointer_declarator_term(
13127    node: Node<'_>,
13128    source: &str,
13129) -> Option<CppTemplateTerm> {
13130    if node.kind() != "ERROR" || node.child_count() == 0 {
13131        return None;
13132    }
13133    let mut children = Vec::new();
13134    for index in 0..node.child_count() {
13135        let child = node.child(index)?;
13136        if child.kind() != "*" {
13137            return None;
13138        }
13139        children.push(CppTemplateTerm::Atom {
13140            kind: "*".to_string(),
13141            text: normalize_cpp_whitespace(node_text(child, source)),
13142        });
13143    }
13144    Some(CppTemplateTerm::Node {
13145        kind: "abstract_pointer_declarator".to_string(),
13146        children,
13147    })
13148}
13149
13150fn cpp_template_expression<'tree>(
13151    node: Node<'tree>,
13152    source: &str,
13153    parameter_names: &[String],
13154    ancestry: &ParentIndex<'tree>,
13155) -> CppTemplateExpression {
13156    let text = normalize_cpp_whitespace(node_text(node, source));
13157    CppTemplateExpression {
13158        text,
13159        term: cpp_template_term(node, source, parameter_names, ancestry),
13160    }
13161}
13162
13163pub fn cpp_template_term<'tree>(
13164    node: Node<'tree>,
13165    source: &str,
13166    parameter_names: &[String],
13167    ancestry: &ParentIndex<'tree>,
13168) -> CppTemplateTerm {
13169    enum Work<'tree> {
13170        Visit(Node<'tree>),
13171        Build { kind: String, child_count: usize },
13172    }
13173
13174    let mut work = vec![Work::Visit(node)];
13175    let mut terms = Vec::new();
13176    while let Some(next) = work.pop() {
13177        match next {
13178            Work::Visit(current) => {
13179                let text = normalize_cpp_whitespace(node_text(current, source));
13180                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
13181                    terms.push(CppTemplateTerm::Parameter(text));
13182                    continue;
13183                }
13184                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
13185                    let mut cursor = current.walk();
13186                    let named = current
13187                        .named_children(&mut cursor)
13188                        .filter(|child| !child.is_extra() && child.kind() != "comment")
13189                        .collect::<Vec<_>>();
13190                    if let [child] = named.as_slice() {
13191                        work.push(Work::Visit(*child));
13192                        continue;
13193                    }
13194                }
13195                if current.child_count() == 0 {
13196                    terms.push(CppTemplateTerm::Atom {
13197                        kind: if matches!(
13198                            current.kind(),
13199                            "identifier"
13200                                | "type_identifier"
13201                                | "field_identifier"
13202                                | "namespace_identifier"
13203                        ) {
13204                            "identifier".to_string()
13205                        } else {
13206                            current.kind().to_string()
13207                        },
13208                        text,
13209                    });
13210                    continue;
13211                }
13212                let children = (0..current.child_count())
13213                    .filter_map(|index| current.child(index))
13214                    .filter(|child| !child.is_extra() && child.kind() != "comment")
13215                    .collect::<Vec<_>>();
13216                work.push(Work::Build {
13217                    kind: current.kind().to_string(),
13218                    child_count: children.len(),
13219                });
13220                work.extend(children.into_iter().rev().map(Work::Visit));
13221            }
13222            Work::Build { kind, child_count } => {
13223                let children = terms.split_off(terms.len() - child_count);
13224                terms.push(CppTemplateTerm::Node { kind, children });
13225            }
13226        }
13227    }
13228    terms.pop().expect("template term traversal emits one root")
13229}
13230
13231fn cpp_template_term_leaf_is_parameter<'tree>(
13232    node: Node<'tree>,
13233    text: &str,
13234    parameter_names: &[String],
13235    ancestry: &ParentIndex<'tree>,
13236) -> bool {
13237    if !parameter_names.iter().any(|parameter| parameter == text) {
13238        return false;
13239    }
13240    !ancestry.parent(node).is_some_and(|parent| {
13241        matches!(
13242            parent.kind(),
13243            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
13244        ) && parent.child_by_field_name("scope").is_some()
13245            && parent.child_by_field_name("name") == Some(node)
13246    })
13247}
13248
13249fn enclosing_cpp_declaration_node<'tree>(
13250    mut node: Node<'tree>,
13251    ancestry: &ParentIndex<'tree>,
13252) -> Option<Node<'tree>> {
13253    loop {
13254        match node.kind() {
13255            "declaration"
13256            | "function_declaration"
13257            | "field_declaration"
13258            | "function_definition" => return Some(node),
13259            _ => node = ancestry.parent(node)?,
13260        }
13261    }
13262}
13263
13264fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
13265    let mut params = Vec::new();
13266    let mut cursor = parameters_node.walk();
13267    for child in parameters_node.children(&mut cursor) {
13268        match child.kind() {
13269            "parameter_declaration" | "optional_parameter_declaration" => {
13270                params.push(cpp_parameter_type(child, source));
13271            }
13272            "variadic_parameter_declaration" => {
13273                params.push(cpp_parameter_type(child, source));
13274            }
13275            "variadic_parameter" | "..." => params.push("...".to_string()),
13276            _ => {}
13277        }
13278    }
13279
13280    if params.is_empty() {
13281        "()".to_string()
13282    } else {
13283        format!("({})", params.join(", "))
13284    }
13285}
13286
13287/// Where a C or C++ callable declaration is written, which is what decides
13288/// which modifier facts its own node is able to state.
13289enum CppCallableScope<'tree> {
13290    /// Written directly in a class, struct or union body. The declaration
13291    /// states its own storage class, and its access is the one the body's
13292    /// specifier ladder is under at that point.
13293    ClassBody {
13294        body: Node<'tree>,
13295        default_visibility: DeclaredVisibility,
13296    },
13297    /// A qualified declaration written outside every class body
13298    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
13299    /// writes no access specifier, and whether the qualifier names a class or
13300    /// a namespace is not decidable from this file alone -- that is exactly
13301    /// what [`crate::reconcile`] needs the include-visible class table for.
13302    Qualified,
13303    /// Namespace scope or file scope, reached by an unqualified name. C has no
13304    /// member functions at all, so every C callable lands here.
13305    Unqualified,
13306}
13307
13308/// Where the declaration sits, from one upward walk of its own ancestry.
13309///
13310/// The walk stops as soon as something proves the declaration is outside every
13311/// class body: a `namespace_definition`, because a class body never encloses
13312/// one, and a `compound_statement`, because a body opens a block scope and
13313/// nothing declared in it is a member of the class the enclosing definition
13314/// belongs to. That is the same rule
13315/// `CppVisitor::visit_anonymous_local_aggregates_in_function` applies when it
13316/// drops the aggregate carrier, and it keeps this question off the O(depth)
13317/// walk a deeply nested generated header would otherwise pay per callable.
13318///
13319/// `function_declarator` is `None` for a declaration whose callable the macro
13320/// recovery reparsed out of a malformed member: there is no declarator node to
13321/// ask whether the name carries a qualifier, and a recovered member is written
13322/// in its class body anyway.
13323fn cpp_callable_scope<'tree>(
13324    declaration: Node<'tree>,
13325    function_declarator: Option<Node<'tree>>,
13326    ancestry: &ParentIndex<'tree>,
13327) -> CppCallableScope<'tree> {
13328    let mut current = Some(declaration);
13329    while let Some(node) = current {
13330        match node.kind() {
13331            "field_declaration_list" => {
13332                let default_visibility =
13333                    ancestry
13334                        .parent(node)
13335                        .map_or(DeclaredVisibility::Unknown, |class_like| {
13336                            // `struct` and `union` members default to public;
13337                            // `class` members default to private.
13338                            match class_like.kind() {
13339                                "struct_specifier" | "union_specifier" => {
13340                                    DeclaredVisibility::Public
13341                                }
13342                                _ => DeclaredVisibility::Private,
13343                            }
13344                        });
13345                return CppCallableScope::ClassBody {
13346                    body: node,
13347                    default_visibility,
13348                };
13349            }
13350            "namespace_definition" | "compound_statement" | "translation_unit" => break,
13351            _ => {}
13352        }
13353        current = ancestry.parent(node);
13354    }
13355    if cpp_callable_declarator_is_qualified(function_declarator) {
13356        CppCallableScope::Qualified
13357    } else {
13358        CppCallableScope::Unqualified
13359    }
13360}
13361
13362/// Whether the declarator's own written name carries a `::` qualifier.
13363///
13364/// A qualified declaration written outside every class body
13365/// (`long Client::send(int) {}`) names a member whose class body this file may
13366/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
13367/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
13368fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
13369    function_declarator
13370        .and_then(cpp_function_declarator_name_node)
13371        .is_some_and(|name| name.kind() == "qualified_identifier")
13372}
13373
13374/// The access one class-body member declaration is written under.
13375///
13376/// C++ states member access positionally: an `access_specifier` applies to
13377/// every member written after it until the next one, and the members before
13378/// the first one take the class keyword's default. The answer is therefore the
13379/// last specifier the body writes before the member's own list-level node,
13380/// read from the specifier's own keyword token rather than from its text.
13381///
13382/// The body's children are read forward with one cursor rather than backwards
13383/// from the member: `Node::prev_sibling` re-descends from the root for each
13384/// step, which is the per-edge cost #2361 keeps off this walk.
13385fn cpp_member_declared_visibility<'tree>(
13386    declaration: Node<'tree>,
13387    body: Node<'tree>,
13388    default_visibility: DeclaredVisibility,
13389    ancestry: &ParentIndex<'tree>,
13390) -> DeclaredVisibility {
13391    let mut list_level = declaration;
13392    while ancestry.parent(list_level) != Some(body) {
13393        let Some(parent) = ancestry.parent(list_level) else {
13394            return default_visibility;
13395        };
13396        list_level = parent;
13397    }
13398    let mut visibility = default_visibility;
13399    let mut cursor = body.walk();
13400    for child in body.children(&mut cursor) {
13401        if child == list_level {
13402            break;
13403        }
13404        if child.kind() == "access_specifier" {
13405            visibility = match child.child(0).map(|keyword| keyword.kind()) {
13406                Some("public") => DeclaredVisibility::Public,
13407                Some("protected") => DeclaredVisibility::Protected,
13408                Some("private") => DeclaredVisibility::Private,
13409                _ => visibility,
13410            };
13411        }
13412    }
13413    visibility
13414}
13415
13416/// Whether this declaration writes the `static` storage class specifier.
13417///
13418/// What `static` *means* is the scope's business: on a class-body member it is
13419/// the receiver fact, and at file scope it is internal linkage, which is why
13420/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
13421/// question and answer it differently.
13422fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
13423    let mut cursor = declaration.walk();
13424    declaration.named_children(&mut cursor).any(|child| {
13425        child.kind() == "storage_class_specifier"
13426            && normalize_cpp_whitespace(node_text(child, source)) == "static"
13427    })
13428}
13429
13430/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
13431/// publishes, as C and C++ state them.
13432struct CppCallableModifiers {
13433    is_static: bool,
13434    is_constructor: bool,
13435    visibility: DeclaredVisibility,
13436}
13437
13438/// The modifier facts a C or C++ callable declaration states in its own shape.
13439///
13440/// C++ has no `static` keyword for receivers and no visibility keyword of the
13441/// Java kind: it states both in the declaration's position. A member declared
13442/// in a class body binds the object it is selected on unless that declaration
13443/// writes the `static` storage class, and its access is the one the body's
13444/// specifier ladder is under. A function at namespace or file scope binds
13445/// nothing and has no access specifier at all, so its declared visibility is
13446/// Unknown. Linkage is recorded separately and does not declare access. C has
13447/// no member functions, and the `static` a C function writes is internal linkage,
13448/// never a receiver fact.
13449///
13450/// Recording these is what lets `receiver_contract_of` answer for a C or C++
13451/// declaration. Without them it reported no contract at all,
13452/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
13453/// callable, no reviewed summary could name one, and every effect or taint walk
13454/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
13455/// in #3455, Kotlin in #3453, Python in #3451.
13456///
13457/// An out-of-line member definition cannot repeat `static` or access. These
13458/// are per-file facts only: the analyzer reconciles the exact overload with
13459/// include-visible class declarations before exposing its receiver contract
13460/// (#3509). A missing declaration leaves that contract unknown.
13461fn cpp_callable_modifiers<'tree>(
13462    declaration: Node<'tree>,
13463    function_declarator: Option<Node<'tree>>,
13464    is_constructor: bool,
13465    source: &str,
13466    ancestry: &ParentIndex<'tree>,
13467) -> CppCallableModifiers {
13468    match cpp_callable_scope(declaration, function_declarator, ancestry) {
13469        CppCallableScope::ClassBody {
13470            body,
13471            default_visibility,
13472        } => CppCallableModifiers {
13473            is_static: cpp_declares_static_storage_class(declaration, source),
13474            is_constructor,
13475            visibility: cpp_member_declared_visibility(
13476                declaration,
13477                body,
13478                default_visibility,
13479                ancestry,
13480            ),
13481        },
13482        CppCallableScope::Qualified => CppCallableModifiers {
13483            is_static: false,
13484            is_constructor,
13485            visibility: DeclaredVisibility::Unknown,
13486        },
13487        CppCallableScope::Unqualified => CppCallableModifiers {
13488            is_static: false,
13489            is_constructor,
13490            visibility: DeclaredVisibility::Unknown,
13491        },
13492    }
13493}
13494
13495/// The persisted signature facts one C or C++ callable declaration publishes.
13496///
13497/// `linkage` is the caller's, not this function's: the node that carries the
13498/// storage class is not always the declaration this declarator sits in -- a
13499/// recovered constructor reads it from its class declaration, and a
13500/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
13501/// visit site that knows which node states it passes the answer in. Recording
13502/// it here keeps it beside the declared visibility it also decides.
13503fn cpp_signature_metadata<'tree>(
13504    signature: String,
13505    function_declarator: Node<'tree>,
13506    linkage: CallableLinkage,
13507    source: &str,
13508    ancestry: &ParentIndex<'tree>,
13509) -> SignatureMetadata {
13510    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
13511    let is_constructor =
13512        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
13513    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
13514        Some(declaration) => cpp_callable_modifiers(
13515            declaration,
13516            Some(function_declarator),
13517            is_constructor,
13518            source,
13519            ancestry,
13520        ),
13521        // Severe recovery left no declaration node above this declarator, so
13522        // nothing is stated here beyond what the callable's own name proves.
13523        None => CppCallableModifiers {
13524            is_static: false,
13525            is_constructor,
13526            visibility: DeclaredVisibility::Unknown,
13527        },
13528    };
13529    let enrich = |mut metadata: SignatureMetadata| {
13530        // #3507: a qualified definition repeats none of the class body's
13531        // dispatch facts, so this row publishes none either.
13532        if let Some(dispatch) = dispatch {
13533            metadata = metadata.with_dispatch_extensibility(dispatch);
13534        }
13535        metadata
13536            .with_callable_linkage(linkage)
13537            .with_callable_modifiers(
13538                modifiers.is_static,
13539                modifiers.is_constructor,
13540                modifiers.visibility,
13541            )
13542    };
13543    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
13544    let return_type_identity =
13545        cpp_callable_return_type_identity(function_declarator, source, ancestry);
13546    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13547        return enrich(
13548            SignatureMetadata::new(signature, Vec::new())
13549                .with_return_type_text(return_type_text)
13550                .with_return_type_identity(return_type_identity),
13551        );
13552    };
13553    let callable_arity = cpp_callable_arity(parameters_node, source);
13554    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
13555    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
13556    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
13557    let Some(relative_start) = signature
13558        .get(search_from..)
13559        .and_then(|suffix| suffix.find(&parameter_text))
13560    else {
13561        return enrich(
13562            SignatureMetadata::new(signature, Vec::new())
13563                .with_callable_arity(callable_arity)
13564                .with_callable_parameter_types(callable_parameter_types)
13565                .with_return_type_text(return_type_text)
13566                .with_return_type_identity(return_type_identity),
13567        );
13568    };
13569    let parameters_start = search_from + relative_start;
13570    let parameters_end = parameters_start + parameter_text.len();
13571    let mut search_start = parameters_start;
13572    let parameters = cpp_parameter_label_nodes(parameters_node)
13573        .into_iter()
13574        .filter_map(|label_node| {
13575            let label = normalize_cpp_whitespace(node_text(label_node, source));
13576            if label.is_empty() || search_start > parameters_end {
13577                return None;
13578            }
13579            let haystack = signature.get(search_start..parameters_end)?;
13580            let relative_start = haystack.find(&label)?;
13581            let start_byte = search_start + relative_start;
13582            let end_byte = start_byte + label.len();
13583            search_start = end_byte;
13584            Some(ParameterMetadata::new(label, start_byte, end_byte))
13585        })
13586        .collect();
13587    enrich(
13588        SignatureMetadata::new(signature, parameters)
13589            .with_callable_arity(callable_arity)
13590            .with_callable_parameter_types(callable_parameter_types)
13591            .with_return_type_text(return_type_text)
13592            .with_return_type_identity(return_type_identity),
13593    )
13594}
13595
13596pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
13597    function_declarator: Node<'tree>,
13598    source: &str,
13599    ancestry: &ParentIndex<'tree>,
13600) -> bool {
13601    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
13602        .or_else(|| function_declarator.child_by_field_name("name"))
13603        .or_else(|| last_named_child(function_declarator))
13604    else {
13605        return false;
13606    };
13607    // An out-of-line definition writes the class in the name itself
13608    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
13609    // terminal component answers without any enclosing class body.
13610    if name_node.kind() == "qualified_identifier" {
13611        let owner = name_node
13612            .child_by_field_name("scope")
13613            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
13614        let member = name_node
13615            .child_by_field_name("name")
13616            .and_then(|name| canonical_cpp_qualified_component(name, source));
13617        return match (owner, member) {
13618            (Some(owner), Some(member)) => owner.name == member.name,
13619            _ => false,
13620        };
13621    }
13622    let Some(callable_name) = direct_identifier_name(name_node, source) else {
13623        return false;
13624    };
13625
13626    let mut current = ancestry.parent(function_declarator);
13627    while let Some(ancestor) = current {
13628        let owner_name = match ancestor.kind() {
13629            "class_specifier" | "struct_specifier" | "union_specifier" => {
13630                class_like_name(ancestor, source, ancestry)
13631            }
13632            "ERROR" => malformed_class_error_owner_name(ancestor, source),
13633            _ => None,
13634        };
13635        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
13636            return true;
13637        }
13638        current = ancestry.parent(ancestor);
13639    }
13640    false
13641}
13642
13643/// Recover the owner name from the direct grammar shape retained when a later
13644/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
13645/// `ERROR` node:
13646///
13647/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
13648///
13649/// Direct-child checks keep this distinct from an unrelated nested class inside
13650/// a broader error region. The closing brace may be displaced past the error
13651/// node, so the opening body token is the available structural boundary.
13652fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
13653    if node.kind() != "ERROR" {
13654        return None;
13655    }
13656    let keyword = node.child(0)?;
13657    if !matches!(keyword.kind(), "class" | "struct" | "union") {
13658        return None;
13659    }
13660    let name_node = node.child(1)?;
13661    let name = direct_identifier_name(name_node, source)?;
13662    let has_body = (2..node.child_count())
13663        .filter_map(|index| node.child(index))
13664        .any(|child| child.kind() == "{");
13665    has_body.then_some(name)
13666}
13667
13668/// The parser-derived return type of one callable declaration.
13669///
13670/// This accepts the enclosing declaration node so consumers do not need to
13671/// duplicate the declarator-unwrapping rules before asking for the structured
13672/// identity.
13673pub fn cpp_callable_declaration_return_type_identity<'tree>(
13674    callable: Node<'tree>,
13675    source: &str,
13676    ancestry: &ParentIndex<'tree>,
13677) -> Option<StructuredTypeIdentity> {
13678    let declarator = callable
13679        .child_by_field_name("declarator")
13680        .and_then(extract_function_declarator)?;
13681    cpp_callable_return_type_identity(declarator, source, ancestry)
13682}
13683
13684pub(crate) fn cpp_callable_return_type_identity<'tree>(
13685    function_declarator: Node<'tree>,
13686    source: &str,
13687    ancestry: &ParentIndex<'tree>,
13688) -> Option<StructuredTypeIdentity> {
13689    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
13690        return None;
13691    }
13692    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
13693    if let Some((return_type, _)) =
13694        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
13695    {
13696        return cpp_structured_type_identity(return_type, source, &lexical_scope);
13697    }
13698    let mut cursor = function_declarator.walk();
13699    if let Some(trailing) = function_declarator
13700        .named_children(&mut cursor)
13701        .find(|child| child.kind() == "trailing_return_type")
13702        && let Some(type_descriptor) = trailing.named_child(0)
13703    {
13704        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
13705    }
13706
13707    let mut current = function_declarator;
13708    let mut wrappers = Vec::new();
13709    while let Some(parent) = ancestry.parent(current) {
13710        if matches!(
13711            parent.kind(),
13712            "function_definition" | "declaration" | "field_declaration"
13713        ) {
13714            let type_node = parent.child_by_field_name("type")?;
13715            if cpp_export_macro_token(node_text(type_node, source))
13716                && (0..parent.named_child_count()).any(|index| {
13717                    parent
13718                        .named_child(index)
13719                        .is_some_and(|child| child.kind() == "ERROR")
13720                })
13721            {
13722                return None;
13723            }
13724            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
13725            for wrapper in wrappers.into_iter().rev() {
13726                identity = cpp_wrap_structured_type(identity, wrapper)?;
13727            }
13728            return Some(identity);
13729        }
13730        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
13731            || (matches!(
13732                parent.kind(),
13733                "pointer_declarator"
13734                    | "reference_declarator"
13735                    | "array_declarator"
13736                    | "parenthesized_declarator"
13737            ) && parent.named_child_count() == 1
13738                && parent.named_child(0) == Some(current));
13739        if !wraps_current_declarator {
13740            return None;
13741        }
13742        match parent.kind() {
13743            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
13744            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
13745            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
13746            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
13747            _ => return None,
13748        }
13749        current = parent;
13750    }
13751    None
13752}
13753
13754fn cpp_structured_type_identity(
13755    node: Node<'_>,
13756    source: &str,
13757    lexical_scope: &[String],
13758) -> Option<StructuredTypeIdentity> {
13759    enum Work<'tree> {
13760        Visit(Node<'tree>),
13761        Wrap(CppStructuredTypeWrapper),
13762        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
13763        BuildGeneric { argument_count: usize },
13764    }
13765
13766    let mut work = vec![Work::Visit(node)];
13767    let mut values = Vec::new();
13768    let mut builder = StructuredTypeIdentityBuilder::default();
13769    while let Some(next) = work.pop() {
13770        match next {
13771            Work::Visit(current) => match current.kind() {
13772                "type_descriptor" => {
13773                    let type_node = current
13774                        .child_by_field_name("type")
13775                        .or_else(|| current.named_child(0))?;
13776                    let mut wrappers = Vec::new();
13777                    let mut cursor = current.walk();
13778                    for child in current.named_children(&mut cursor) {
13779                        if child.id() != type_node.id() {
13780                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
13781                        }
13782                    }
13783                    work.push(Work::ApplyWrappers(wrappers));
13784                    work.push(Work::Visit(type_node));
13785                }
13786                "pointer_declarator" | "abstract_pointer_declarator" => {
13787                    let child = current
13788                        .child_by_field_name("declarator")
13789                        .or_else(|| current.named_child(0))?;
13790                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
13791                    work.push(Work::Visit(child));
13792                }
13793                "reference_declarator" => {
13794                    let child = current
13795                        .child_by_field_name("declarator")
13796                        .or_else(|| current.named_child(0))?;
13797                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
13798                    work.push(Work::Visit(child));
13799                }
13800                "array_declarator" | "abstract_array_declarator" => {
13801                    let child = current
13802                        .child_by_field_name("declarator")
13803                        .or_else(|| current.named_child(0))?;
13804                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
13805                    work.push(Work::Visit(child));
13806                }
13807                "template_type" => {
13808                    let name_node = current.child_by_field_name("name")?;
13809                    let arguments = current
13810                        .child_by_field_name("arguments")
13811                        .map(|arguments_node| {
13812                            let mut cursor = arguments_node.walk();
13813                            arguments_node
13814                                .named_children(&mut cursor)
13815                                .filter(|child| !child.is_extra() && child.kind() != "comment")
13816                                .collect::<Vec<_>>()
13817                        })
13818                        .unwrap_or_default();
13819                    work.push(Work::BuildGeneric {
13820                        argument_count: arguments.len(),
13821                    });
13822                    work.extend(arguments.into_iter().rev().map(Work::Visit));
13823                    work.push(Work::Visit(name_node));
13824                }
13825                "qualified_identifier"
13826                | "scoped_identifier"
13827                | "scoped_type_identifier"
13828                | "type_identifier"
13829                | "field_identifier"
13830                | "identifier"
13831                | "namespace_identifier"
13832                | "primitive_type" => {
13833                    values.push(builder.named(cpp_structured_named_type(
13834                        current,
13835                        source,
13836                        lexical_scope,
13837                    )?)?);
13838                }
13839                _ => {
13840                    let child = current.child_by_field_name("type").or_else(|| {
13841                        (current.named_child_count() == 1)
13842                            .then(|| current.named_child(0))
13843                            .flatten()
13844                    })?;
13845                    work.push(Work::Visit(child));
13846                }
13847            },
13848            Work::Wrap(wrapper) => {
13849                let root = values.pop()?;
13850                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
13851            }
13852            Work::ApplyWrappers(wrappers) => {
13853                let mut root = values.pop()?;
13854                for wrapper in wrappers.into_iter().rev() {
13855                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
13856                }
13857                values.push(root);
13858            }
13859            Work::BuildGeneric { argument_count } => {
13860                let value_count = argument_count.checked_add(1)?;
13861                let start = values.len().checked_sub(value_count)?;
13862                let mut built = values.split_off(start);
13863                let base = built.remove(0);
13864                values.push(builder.generic(base, built)?);
13865            }
13866        }
13867    }
13868    (values.len() == 1)
13869        .then(|| values.pop())
13870        .flatten()
13871        .and_then(|root| builder.finish(root))
13872}
13873
13874fn cpp_structured_named_type(
13875    node: Node<'_>,
13876    source: &str,
13877    lexical_scope: &[String],
13878) -> Option<StructuredTypeName> {
13879    let path = cpp_structured_type_path(node, source)?;
13880    let absolute = node.child_by_field_name("scope").is_none()
13881        && node.child(0).is_some_and(|child| child.kind() == "::");
13882    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
13883}
13884
13885#[derive(Clone, Copy)]
13886enum CppStructuredTypeWrapper {
13887    Pointer,
13888    LvalueReference,
13889    RvalueReference,
13890    Array,
13891}
13892
13893fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
13894    let mut wrappers = Vec::new();
13895    let mut current = node;
13896    loop {
13897        match current.kind() {
13898            "pointer_declarator" | "abstract_pointer_declarator" => {
13899                wrappers.push(CppStructuredTypeWrapper::Pointer)
13900            }
13901            "reference_declarator" | "abstract_reference_declarator" => {
13902                wrappers.push(cpp_reference_wrapper(current)?);
13903            }
13904            "array_declarator" | "abstract_array_declarator" => {
13905                wrappers.push(CppStructuredTypeWrapper::Array)
13906            }
13907            // An initializer and a grouping parenthesis are not type
13908            // structure. Stopping at one of them dropped every wrapper an
13909            // initialized or parenthesized declarator writes, so
13910            // `std::string &alias = value;` reported the unqualified
13911            // `std::string` while `const std::string &value` as a parameter
13912            // reported the reference it shares.
13913            "init_declarator" | "parenthesized_declarator" => {}
13914            _ => break,
13915        }
13916        let Some(child) = current
13917            .child_by_field_name("declarator")
13918            .or_else(|| current.named_child(0))
13919        else {
13920            break;
13921        };
13922        current = child;
13923    }
13924    Some(wrappers)
13925}
13926
13927fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
13928    node.children(&mut node.walk())
13929        .find_map(|child| match child.kind() {
13930            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
13931            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
13932            _ => None,
13933        })
13934}
13935
13936fn cpp_wrap_structured_type(
13937    identity: StructuredTypeIdentity,
13938    wrapper: CppStructuredTypeWrapper,
13939) -> Option<StructuredTypeIdentity> {
13940    match wrapper {
13941        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
13942        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
13943        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
13944        CppStructuredTypeWrapper::Array => identity.wrap_array(),
13945    }
13946}
13947
13948fn cpp_wrap_structured_type_node(
13949    builder: &mut StructuredTypeIdentityBuilder,
13950    inner: StructuredTypeNodeId,
13951    wrapper: CppStructuredTypeWrapper,
13952) -> Option<StructuredTypeNodeId> {
13953    match wrapper {
13954        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
13955        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
13956        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
13957        CppStructuredTypeWrapper::Array => builder.array(inner),
13958    }
13959}
13960
13961/// The dot-free name components a qualified C++ name node writes, outermost
13962/// first.
13963///
13964/// The walk reads the grammar's `scope` and `name` fields rather than cutting
13965/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
13966/// shape the grammar does not name yields nothing at all.
13967pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
13968    let mut path = Vec::new();
13969    let mut stack = vec![node];
13970    while let Some(current) = stack.pop() {
13971        match current.kind() {
13972            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
13973                let component = node_text(current, source).to_string();
13974                if component.is_empty() {
13975                    return None;
13976                }
13977                path.push(component);
13978            }
13979            "template_type" | "dependent_type" => {
13980                stack.push(current.child_by_field_name("name")?);
13981            }
13982            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13983                stack.push(current.child_by_field_name("name")?);
13984                if let Some(scope) = current.child_by_field_name("scope") {
13985                    stack.push(scope);
13986                }
13987            }
13988            _ => return None,
13989        }
13990    }
13991    (!path.is_empty()).then_some(path)
13992}
13993
13994fn cpp_callable_lexical_scope<'tree>(
13995    node: Node<'tree>,
13996    source: &str,
13997    ancestry: &ParentIndex<'tree>,
13998) -> Vec<String> {
13999    let mut groups = Vec::new();
14000    let mut current = ancestry.parent(node);
14001    while let Some(parent) = current {
14002        if matches!(
14003            parent.kind(),
14004            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
14005        ) && let Some(name_node) = parent.child_by_field_name("name")
14006            && let Some(components) = cpp_structured_type_path(name_node, source)
14007            && !components.is_empty()
14008        {
14009            groups.push(components);
14010        }
14011        current = ancestry.parent(parent);
14012    }
14013    groups.reverse();
14014    groups.into_iter().flatten().collect()
14015}
14016
14017/// Whether a call on this callable's static target can select another body.
14018///
14019/// C++ says this where the member is *declared*, not where it is defined, and
14020/// that is the whole difficulty of the question: virtual-ness is inherited
14021/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
14022/// `Base::run` is), an out-of-line definition repeats none of it, and whether
14023/// a definition's qualifier names a class or a namespace is a class-table
14024/// question. So a row answers `Closed` only when its own node proves it:
14025///
14026/// * a written `final` is the one specifier that ends the family here, and it
14027///   is the only thing that closes a member of a class that has a base clause;
14028/// * a class-body member is `Closed` otherwise only when its class body writes
14029///   no base clause at all, because a base clause is the only evidence in this
14030///   file that some base may declare this member virtual;
14031/// * anything with a virtual-family boundary, a template head, or a
14032///   preprocessor or error region is `Open`;
14033/// * a qualified declaration outside every class body answers `None`: its
14034///   class body, which is where the fact is written, may be in another file.
14035///
14036/// The analyzer joins the `None` rows with the include-visible class-body rows
14037/// of the same logical callable (#3507), the same join #3509 makes for a
14038/// definition's declared access; a row that keeps `None` after that join stays
14039/// unknown and no dispatch proof may be built on it.
14040fn cpp_callable_dispatch_extensibility<'tree>(
14041    function_declarator: Node<'tree>,
14042    ancestry: &ParentIndex<'tree>,
14043) -> Option<DispatchExtensibility> {
14044    let mut declaration = None;
14045    // The first class body this declarator is written in. The upward walk
14046    // visits it anyway, so only the one step from the body to its class node
14047    // is extra, and only for a class-body member.
14048    let mut class_body = None;
14049    let mut current = Some(function_declarator);
14050    while let Some(node) = current {
14051        match node.kind() {
14052            "template_declaration"
14053            | "preproc_if"
14054            | "preproc_ifdef"
14055            | "preproc_else"
14056            | "preproc_elif"
14057            | "preproc_call"
14058            | "ERROR" => return Some(DispatchExtensibility::Open),
14059            "declaration" | "field_declaration" | "function_definition" => {
14060                declaration.get_or_insert(node);
14061            }
14062            "field_declaration_list" => {
14063                class_body.get_or_insert(node);
14064            }
14065            "translation_unit" => break,
14066            _ => {}
14067        }
14068        current = ancestry.parent(node);
14069    }
14070    let Some(declaration) = declaration else {
14071        return Some(DispatchExtensibility::Open);
14072    };
14073
14074    let mut saw_virtual_boundary = false;
14075    let mut stack = vec![declaration];
14076    while let Some(node) = stack.pop() {
14077        match node.kind() {
14078            "compound_statement" | "field_declaration_list" => continue,
14079            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
14080            "virtual"
14081            | "override"
14082            | "virtual_specifier"
14083            | "pure_virtual_clause"
14084            | "template_parameter_list"
14085            | "template_method"
14086            | "template_function"
14087            | "ERROR" => saw_virtual_boundary = true,
14088            _ => {}
14089        }
14090        let mut cursor = node.walk();
14091        stack.extend(node.children(&mut cursor));
14092    }
14093
14094    if saw_virtual_boundary {
14095        return Some(DispatchExtensibility::Open);
14096    }
14097    if let Some(body) = class_body {
14098        // C++ inherits virtual-ness, so a member written without a `virtual`
14099        // keyword is still virtual when a base declares it. Whether some base
14100        // does is not decidable here, so a base clause keeps the answer open.
14101        let has_base_clause = ancestry
14102            .parent(body)
14103            .and_then(cpp_class_like_base_clause)
14104            .is_some();
14105        return Some(if has_base_clause {
14106            DispatchExtensibility::Open
14107        } else {
14108            DispatchExtensibility::Closed
14109        });
14110    }
14111    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
14112        return None;
14113    }
14114    // A namespace or file scope callable is reached by name, never through a
14115    // receiver, so no other body can be selected for a call to it.
14116    Some(DispatchExtensibility::Closed)
14117}
14118
14119fn cpp_callable_linkage<'tree>(
14120    declaration: Node<'tree>,
14121    source: &str,
14122    ancestry: &ParentIndex<'tree>,
14123) -> CallableLinkage {
14124    let mut enclosed_by_class = false;
14125    let mut current = ancestry.parent(declaration);
14126    while let Some(node) = current {
14127        if node.kind() == "namespace_definition"
14128            && node
14129                .child_by_field_name("name")
14130                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14131        {
14132            return CallableLinkage::Internal;
14133        }
14134        if matches!(
14135            node.kind(),
14136            "class_specifier" | "struct_specifier" | "union_specifier"
14137        ) {
14138            if node
14139                .child_by_field_name("name")
14140                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14141            {
14142                return CallableLinkage::Internal;
14143            }
14144            enclosed_by_class = true;
14145        }
14146        // An export macro between `class` and the class name can make
14147        // tree-sitter parse the entire class as a function definition. The
14148        // structured recovery proves that this container is a named class
14149        // scope, not a local callable scope.
14150        if node.kind() == "lambda_expression"
14151            || node.kind() == "function_definition"
14152                && !is_recovered_exported_class_container(node, source)
14153        {
14154            return CallableLinkage::Internal;
14155        }
14156        current = ancestry.parent(node);
14157    }
14158
14159    if enclosed_by_class {
14160        return CallableLinkage::External;
14161    }
14162
14163    if cpp_declares_static_storage_class(declaration, source) {
14164        CallableLinkage::Internal
14165    } else {
14166        CallableLinkage::External
14167    }
14168}
14169
14170fn cpp_callable_return_type_text<'tree>(
14171    function_declarator: Node<'tree>,
14172    source: &str,
14173    ancestry: &ParentIndex<'tree>,
14174) -> Option<String> {
14175    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14176        return None;
14177    }
14178    if let Some((return_type, _)) =
14179        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14180    {
14181        let text = normalize_cpp_whitespace(node_text(return_type, source));
14182        return (!text.is_empty()).then_some(text);
14183    }
14184    let mut cursor = function_declarator.walk();
14185    if let Some(trailing) = function_declarator
14186        .named_children(&mut cursor)
14187        .find(|child| child.kind() == "trailing_return_type")
14188        && let Some(type_descriptor) = trailing.named_child(0)
14189    {
14190        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
14191        if !text.is_empty() {
14192            return Some(text);
14193        }
14194    }
14195
14196    let mut current = function_declarator;
14197    let mut indirection = String::new();
14198    while let Some(parent) = ancestry.parent(current) {
14199        if matches!(
14200            parent.kind(),
14201            "function_definition" | "declaration" | "field_declaration"
14202        ) {
14203            let type_node = parent.child_by_field_name("type")?;
14204            if cpp_export_macro_token(node_text(type_node, source))
14205                && (0..parent.named_child_count()).any(|index| {
14206                    parent
14207                        .named_child(index)
14208                        .is_some_and(|child| child.kind() == "ERROR")
14209                })
14210            {
14211                // Export/decorator macros commonly occupy the grammar's `type`
14212                // field and leave the semantic return type in an ERROR sibling.
14213                // Do not persist the macro token as a return type. The malformed
14214                // declaration does not carry enough structured evidence here.
14215                return None;
14216            }
14217            let base = normalize_cpp_whitespace(node_text(type_node, source));
14218            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
14219        }
14220        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14221            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
14222                && parent.named_child_count() == 1
14223                && parent.named_child(0) == Some(current));
14224        if wraps_current_declarator {
14225            match parent.kind() {
14226                "pointer_declarator" => indirection.push('*'),
14227                "reference_declarator" => {
14228                    let reference = parent
14229                        .children(&mut parent.walk())
14230                        .find(|child| !child.is_named())
14231                        .map(|child| node_text(child, source))
14232                        .unwrap_or("&");
14233                    indirection.push_str(reference);
14234                }
14235                "init_declarator" | "parenthesized_declarator" => {}
14236                _ => return None,
14237            }
14238            current = parent;
14239            continue;
14240        }
14241        return None;
14242    }
14243    None
14244}
14245
14246pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
14247    let mut required = 0;
14248    let mut total = 0;
14249    let mut repeated = false;
14250    let mut cursor = parameters_node.walk();
14251    for child in parameters_node.children(&mut cursor) {
14252        match child.kind() {
14253            "parameter_declaration" => {
14254                if cpp_parameter_is_explicit_object(child, source) {
14255                    continue;
14256                }
14257                if child.child_by_field_name("declarator").is_none()
14258                    && child
14259                        .child_by_field_name("type")
14260                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
14261                {
14262                    continue;
14263                }
14264                required += 1;
14265                total += 1;
14266            }
14267            "optional_parameter_declaration" => total += 1,
14268            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14269                repeated = true;
14270            }
14271            _ => {}
14272        }
14273    }
14274    CallableArity::new(required, total, repeated)
14275}
14276
14277fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
14278    parameter
14279        .child_by_field_name("type")
14280        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
14281        .and_then(|type_node| type_node.child_by_field_name("constraint"))
14282        .is_some_and(|constraint| {
14283            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
14284        })
14285}
14286
14287/// One entry of a callable's invocation parameter list.
14288///
14289/// The list excludes an explicit object parameter and a lone `void`, so its
14290/// length is the callable's invocation arity. Every derivation of a parameter
14291/// type - the rendered spelling used for overload discrimination and the
14292/// structured identity used by dependency-pack production - starts from this
14293/// same sequence, so the two can never disagree about which parameters exist.
14294#[derive(Clone, Copy)]
14295enum CppParameterSlot<'tree> {
14296    Declared(Node<'tree>),
14297    Ellipsis,
14298}
14299
14300fn cpp_callable_parameter_slots<'tree>(
14301    parameters_node: Node<'tree>,
14302    source: &str,
14303) -> Vec<CppParameterSlot<'tree>> {
14304    let mut slots = Vec::new();
14305    let mut cursor = parameters_node.walk();
14306    for parameter in parameters_node.children(&mut cursor) {
14307        match parameter.kind() {
14308            "parameter_declaration" | "optional_parameter_declaration" => {
14309                if cpp_parameter_is_explicit_object(parameter, source)
14310                    || (parameter.child_by_field_name("declarator").is_none()
14311                        && parameter
14312                            .child_by_field_name("type")
14313                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
14314                {
14315                    continue;
14316                }
14317                slots.push(CppParameterSlot::Declared(parameter));
14318            }
14319            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14320                slots.push(CppParameterSlot::Ellipsis);
14321            }
14322            _ => {}
14323        }
14324    }
14325    slots
14326}
14327
14328pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
14329    cpp_callable_parameter_slots(parameters_node, source)
14330        .into_iter()
14331        .map(|slot| match slot {
14332            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
14333            CppParameterSlot::Ellipsis => "...".to_string(),
14334        })
14335        .collect()
14336}
14337
14338/// One callable parameter's parser-derived type.
14339///
14340/// A rendered spelling such as `const T&` is a source text, not a type name. A
14341/// consumer that must publish a type into a structured model - a semantic-pack
14342/// type reference, for example - reads this instead.
14343#[derive(Debug, Clone, PartialEq, Eq)]
14344pub enum CppParameterType {
14345    /// The written type reduced to a structured identity. C++ cv-qualifiers
14346    /// have no place in that model and are not represented.
14347    Structured(StructuredTypeIdentity),
14348    /// A `...` pack, which declares no parameter type at all.
14349    Ellipsis,
14350    /// A written type with no structured reduction, such as a macro-obscured,
14351    /// `decltype`-computed, or function-pointer parameter.
14352    Unstructured,
14353}
14354
14355/// The structured type of each invocation parameter, in declaration order.
14356///
14357/// The result is index-parallel with the rendered
14358/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
14359/// callable.
14360pub fn cpp_callable_parameter_type_identities<'tree>(
14361    function_declarator: Node<'tree>,
14362    source: &str,
14363    ancestry: &ParentIndex<'tree>,
14364) -> Vec<CppParameterType> {
14365    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14366        return Vec::new();
14367    };
14368    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14369    cpp_callable_parameter_slots(parameters_node, source)
14370        .into_iter()
14371        .map(|slot| match slot {
14372            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
14373            CppParameterSlot::Declared(parameter) => {
14374                cpp_parameter_type_identity(parameter, source, &lexical_scope)
14375                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
14376            }
14377        })
14378        .collect()
14379}
14380
14381/// The parser-derived type of one declared object or parameter.
14382///
14383/// The declaration owns the base `type` field while the individual declarator
14384/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
14385/// lets callers distinguish several declarators in one declaration without
14386/// reparsing a rendered signature.
14387pub fn cpp_declaration_type_identity<'tree>(
14388    declaration: Node<'tree>,
14389    declarator: Node<'tree>,
14390    source: &str,
14391    ancestry: &ParentIndex<'tree>,
14392) -> Option<StructuredTypeIdentity> {
14393    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
14394    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
14395}
14396
14397fn cpp_parameter_type_identity(
14398    parameter: Node<'_>,
14399    source: &str,
14400    lexical_scope: &[String],
14401) -> Option<StructuredTypeIdentity> {
14402    cpp_declaration_type_identity_in_scope(
14403        parameter,
14404        cpp_parameter_declarator(parameter),
14405        source,
14406        lexical_scope,
14407    )
14408}
14409
14410fn cpp_declaration_type_identity_in_scope(
14411    declaration: Node<'_>,
14412    declarator: Option<Node<'_>>,
14413    source: &str,
14414    lexical_scope: &[String],
14415) -> Option<StructuredTypeIdentity> {
14416    let type_node = declaration.child_by_field_name("type")?;
14417    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
14418    if let Some(declarator) = declarator {
14419        for wrapper in cpp_structured_declarator_wrappers(declarator)?
14420            .into_iter()
14421            .rev()
14422        {
14423            identity = cpp_wrap_structured_type(identity, wrapper)?;
14424        }
14425    }
14426    Some(identity)
14427}
14428
14429/// The comparable shape of each invocation parameter, in declaration order.
14430///
14431/// The result is index-parallel with
14432/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
14433/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
14434/// must treat as evidence of nothing rather than as agreement.
14435pub fn cpp_comparable_parameter_shapes<'tree>(
14436    function_declarator: Node<'tree>,
14437    source: &str,
14438    ancestry: &ParentIndex<'tree>,
14439) -> Vec<CppComparableSlot> {
14440    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14441        return Vec::new();
14442    };
14443    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14444    cpp_callable_parameter_slots(parameters_node, source)
14445        .into_iter()
14446        .map(|slot| match slot {
14447            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
14448            CppParameterSlot::Declared(parameter) => {
14449                cpp_comparable_parameter(parameter, source, &lexical_scope)
14450                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
14451            }
14452        })
14453        .collect()
14454}
14455
14456fn cpp_comparable_parameter(
14457    parameter: Node<'_>,
14458    source: &str,
14459    lexical_scope: &[String],
14460) -> Option<CppComparableParameter> {
14461    let type_node = parameter.child_by_field_name("type")?;
14462    let levels = match cpp_parameter_declarator(parameter) {
14463        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14464        None => Vec::new(),
14465    };
14466    let mut shape = cpp_comparable_type_shape(
14467        type_node,
14468        cpp_cv_qualifiers(parameter, source),
14469        levels,
14470        source,
14471        lexical_scope,
14472    )?;
14473    shape.adjust_parameter_top_level();
14474    Some(shape)
14475}
14476
14477/// The `const` and `volatile` qualifiers written as direct named children of
14478/// `node`.
14479///
14480/// The grammar exposes `type_qualifier` as a non-field named child in exactly
14481/// the three places a parameter's qualifiers can be written: on the
14482/// `parameter_declaration` itself (the base type), on a `type_descriptor`
14483/// (inside a template argument list), and on each `pointer_declarator` level
14484/// (the pointer object). Every other qualifier the grammar admits - `restrict`
14485/// and friends - takes no part in C++ type identity, the same filter
14486/// `cpp_parameter_type` applies to the rendered spelling (#1827).
14487fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
14488    let mut qualifiers = CppCvQualifiers::default();
14489    let mut cursor = node.walk();
14490    for child in node.named_children(&mut cursor) {
14491        if child.kind() != "type_qualifier" {
14492            continue;
14493        }
14494        match node_text(child, source) {
14495            "const" => qualifiers.konst = true,
14496            "volatile" => qualifiers.volatil = true,
14497            _ => {}
14498        }
14499    }
14500    qualifiers
14501}
14502
14503#[derive(Clone, Copy, Default)]
14504struct CppCvQualifiers {
14505    konst: bool,
14506    volatil: bool,
14507}
14508
14509impl CppCvQualifiers {
14510    fn union(self, other: Self) -> Self {
14511        Self {
14512            konst: self.konst || other.konst,
14513            volatil: self.volatil || other.volatil,
14514        }
14515    }
14516}
14517
14518/// One pointer, reference or array level a declarator chain adds.
14519#[derive(Clone, Copy)]
14520enum CppComparableLevel {
14521    Pointer { konst: bool, volatil: bool },
14522    Reference,
14523    Array,
14524}
14525
14526/// The levels `declarator` adds, outermost written level first.
14527///
14528/// C++ declarator syntax binds inside out: the level written closest to the
14529/// declared name is the outermost type constructor, and tree-sitter nests it
14530/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
14531/// applies in order to reach "array of pointer to int", and the qualifier of
14532/// `int * const *p` is read on the level it was written next to, the inner
14533/// pointer of the resulting type.
14534///
14535/// A declarator chain that names a function type - a function-pointer
14536/// parameter - has no comparable shape and reports `None`, matching the
14537/// structured identity channel.
14538fn cpp_comparable_declarator_levels(
14539    declarator: Node<'_>,
14540    source: &str,
14541) -> Option<Vec<CppComparableLevel>> {
14542    let mut levels = Vec::new();
14543    let mut current = declarator;
14544    loop {
14545        match current.kind() {
14546            "pointer_declarator" | "abstract_pointer_declarator" => {
14547                let qualifiers = cpp_cv_qualifiers(current, source);
14548                levels.push(CppComparableLevel::Pointer {
14549                    konst: qualifiers.konst,
14550                    volatil: qualifiers.volatil,
14551                });
14552            }
14553            "reference_declarator" | "abstract_reference_declarator" => {
14554                levels.push(CppComparableLevel::Reference);
14555            }
14556            "array_declarator" | "abstract_array_declarator" => {
14557                levels.push(CppComparableLevel::Array);
14558            }
14559            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
14560            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
14561            _ => return None,
14562        }
14563        let Some(next) = cpp_nested_declarator(current) else {
14564            return Some(levels);
14565        };
14566        current = next;
14567    }
14568}
14569
14570/// Reduce one written type to a comparable arena.
14571///
14572/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
14573/// two additions: each visited type node carries the cv-qualifiers written on
14574/// it, and declarator levels arrive as a prepared list rather than being
14575/// rediscovered inside the walk.
14576fn cpp_comparable_type_shape(
14577    type_node: Node<'_>,
14578    qualifiers: CppCvQualifiers,
14579    levels: Vec<CppComparableLevel>,
14580    source: &str,
14581    lexical_scope: &[String],
14582) -> Option<CppComparableParameter> {
14583    enum Work<'tree> {
14584        Visit {
14585            node: Node<'tree>,
14586            qualifiers: CppCvQualifiers,
14587        },
14588        ApplyLevels(Vec<CppComparableLevel>),
14589        BuildGeneric {
14590            argument_count: usize,
14591        },
14592    }
14593
14594    let mut nodes: Vec<CppComparableNode> = Vec::new();
14595    let mut values: Vec<usize> = Vec::new();
14596    let mut work = vec![
14597        Work::ApplyLevels(levels),
14598        Work::Visit {
14599            node: type_node,
14600            qualifiers,
14601        },
14602    ];
14603    while let Some(next) = work.pop() {
14604        match next {
14605            Work::Visit { node, qualifiers } => match node.kind() {
14606                "type_descriptor" => {
14607                    let inner_type = node
14608                        .child_by_field_name("type")
14609                        .or_else(|| node.named_child(0))?;
14610                    let mut cursor = node.walk();
14611                    let declarator = node.child_by_field_name("declarator").or_else(|| {
14612                        node.named_children(&mut cursor).find(|child| {
14613                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
14614                        })
14615                    });
14616                    let levels = match declarator {
14617                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14618                        None => Vec::new(),
14619                    };
14620                    work.push(Work::ApplyLevels(levels));
14621                    work.push(Work::Visit {
14622                        node: inner_type,
14623                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
14624                    });
14625                }
14626                "sized_type_specifier" => {
14627                    // `unsigned char` is one primitive type whose components are
14628                    // partly unnamed tokens, so the whole specifier is its own
14629                    // name component. Reducing it to the `type` child would make
14630                    // `f(unsigned char)` and `f(char)` compare equal.
14631                    let name = StructuredTypeName::new(
14632                        vec![normalize_cpp_whitespace(node_text(node, source))],
14633                        lexical_scope.to_vec(),
14634                        false,
14635                    )?;
14636                    values.push(cpp_push_comparable_node(
14637                        &mut nodes,
14638                        CppComparableNode::Named {
14639                            name,
14640                            primitive: true,
14641                            konst: qualifiers.konst,
14642                            volatil: qualifiers.volatil,
14643                        },
14644                    ));
14645                }
14646                "qualified_identifier"
14647                | "scoped_identifier"
14648                | "scoped_type_identifier"
14649                | "type_identifier"
14650                | "field_identifier"
14651                | "identifier"
14652                | "namespace_identifier"
14653                | "primitive_type"
14654                | "template_type" => {
14655                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
14656                    values.push(cpp_push_comparable_node(
14657                        &mut nodes,
14658                        CppComparableNode::Named {
14659                            name,
14660                            primitive: node.kind() == "primitive_type",
14661                            konst: qualifiers.konst,
14662                            volatil: qualifiers.volatil,
14663                        },
14664                    ));
14665                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
14666                        let mut cursor = arguments_node.walk();
14667                        let arguments = arguments_node
14668                            .named_children(&mut cursor)
14669                            .filter(|child| !child.is_extra() && child.kind() != "comment")
14670                            .collect::<Vec<_>>();
14671                        work.push(Work::BuildGeneric {
14672                            argument_count: arguments.len(),
14673                        });
14674                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
14675                            node: argument,
14676                            qualifiers: CppCvQualifiers::default(),
14677                        }));
14678                    }
14679                }
14680                _ => {
14681                    let inner = node.child_by_field_name("type").or_else(|| {
14682                        (node.named_child_count() == 1)
14683                            .then(|| node.named_child(0))
14684                            .flatten()
14685                    })?;
14686                    work.push(Work::Visit {
14687                        node: inner,
14688                        qualifiers,
14689                    });
14690                }
14691            },
14692            Work::ApplyLevels(levels) => {
14693                let mut root = values.pop()?;
14694                for level in levels {
14695                    let node = match level {
14696                        CppComparableLevel::Pointer { konst, volatil } => {
14697                            CppComparableNode::Pointer {
14698                                inner: root,
14699                                konst,
14700                                volatil,
14701                            }
14702                        }
14703                        CppComparableLevel::Reference => {
14704                            CppComparableNode::Reference { inner: root }
14705                        }
14706                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
14707                    };
14708                    root = cpp_push_comparable_node(&mut nodes, node);
14709                }
14710                values.push(root);
14711            }
14712            Work::BuildGeneric { argument_count } => {
14713                let value_count = argument_count.checked_add(1)?;
14714                let start = values.len().checked_sub(value_count)?;
14715                let mut built = values.split_off(start);
14716                let base = built.remove(0);
14717                values.push(cpp_push_comparable_node(
14718                    &mut nodes,
14719                    CppComparableNode::Generic {
14720                        base,
14721                        arguments: built,
14722                    },
14723                ));
14724            }
14725        }
14726    }
14727    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
14728    debug_assert_eq!(
14729        root,
14730        nodes.len().saturating_sub(1),
14731        "comparable nodes are appended in post-order, so the root is the last one"
14732    );
14733    Some(CppComparableParameter::new(nodes, root))
14734}
14735
14736fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
14737    nodes.push(node);
14738    nodes.len() - 1
14739}
14740
14741/// The template argument list of the name `node` terminates in, if any.
14742///
14743/// `std::vector<int>` writes its arguments on the `name` of a qualified
14744/// identifier, so a walk that stopped at the qualified node would reduce
14745/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
14746fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
14747    let mut current = node;
14748    loop {
14749        match current.kind() {
14750            "template_type" => return current.child_by_field_name("arguments"),
14751            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14752                current = current.child_by_field_name("name")?;
14753            }
14754            _ => return None,
14755        }
14756    }
14757}
14758
14759/// The callable declarator of the declaration that covers `start_byte`.
14760///
14761/// A consumer that holds a declaration's recorded byte position rather than its
14762/// syntax node - external header extraction, for instance - uses this to reach
14763/// the same `function_declarator` the declaration walk read.
14764pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
14765    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
14766    loop {
14767        if matches!(
14768            current.kind(),
14769            "declaration" | "field_declaration" | "function_definition"
14770        ) && let Some(declarator) = current
14771            .child_by_field_name("declarator")
14772            .and_then(extract_function_declarator)
14773        {
14774            return Some(declarator);
14775        }
14776        current = current.parent()?;
14777    }
14778}
14779
14780fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
14781    let mut labels = Vec::new();
14782    let mut cursor = parameters_node.walk();
14783    for child in parameters_node.children(&mut cursor) {
14784        match child.kind() {
14785            "parameter_declaration" | "optional_parameter_declaration" => {
14786                if let Some(name_node) = child
14787                    .child_by_field_name("declarator")
14788                    .and_then(cpp_declarator_label_node)
14789                {
14790                    labels.push(name_node);
14791                } else {
14792                    labels.push(child);
14793                }
14794            }
14795            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14796                labels.push(child);
14797            }
14798            _ => {}
14799        }
14800    }
14801    labels
14802}
14803
14804fn cpp_signature_search_start<'tree>(
14805    signature: &str,
14806    function_declarator: Node<'tree>,
14807    source: &str,
14808    ancestry: &ParentIndex<'tree>,
14809) -> usize {
14810    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
14811        return 0;
14812    };
14813    let raw = node_text(enclosing, source);
14814    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
14815    let offset = function_declarator
14816        .start_byte()
14817        .saturating_sub(enclosing.start_byte())
14818        .saturating_sub(leading_trim_bytes);
14819    offset.min(signature.len())
14820}
14821
14822fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
14823    match node.kind() {
14824        "identifier" | "field_identifier" => Some(node),
14825        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
14826            .child_by_field_name("declarator")
14827            .or_else(|| last_named_child(node))
14828            .and_then(cpp_declarator_label_node),
14829        "array_declarator" => node
14830            .child_by_field_name("declarator")
14831            .and_then(cpp_declarator_label_node),
14832        "function_declarator" => node
14833            .child_by_field_name("declarator")
14834            .or_else(|| node.child_by_field_name("name"))
14835            .or_else(|| last_named_child(node))
14836            .and_then(cpp_declarator_label_node),
14837        _ => None,
14838    }
14839}
14840
14841fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
14842    let base_type = parameter
14843        .child_by_field_name("type")
14844        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
14845        .unwrap_or_default();
14846    let declarator = cpp_parameter_declarator(parameter);
14847    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
14848    // are discarded, so `f(const int)` and `f(int)` declare one function. A
14849    // qualifier written next to the parameter's type is only top-level when
14850    // the declarator adds no indirection; behind a pointer, reference or array
14851    // declarator the same qualifier belongs to the pointee, referent or
14852    // element and keeps distinguishing the type (#1827).
14853    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
14854    let mut cursor = parameter.walk();
14855    let qualifiers = parameter
14856        .named_children(&mut cursor)
14857        .filter(|child| child.kind() == "type_qualifier")
14858        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
14859        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
14860        .collect::<Vec<_>>()
14861        .join(" ");
14862    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
14863        (true, _) => base_type,
14864        (_, true) => qualifiers,
14865        (false, false) => format!("{qualifiers} {base_type}"),
14866    };
14867    let declarator_suffix = declarator
14868        .map(|node| cpp_declarator_suffix_without_name(node, source))
14869        .unwrap_or_default();
14870
14871    let combined = if type_text.is_empty() {
14872        declarator_suffix
14873    } else if declarator_suffix.is_empty() {
14874        type_text
14875    } else {
14876        format!("{type_text} {declarator_suffix}")
14877    };
14878    normalize_cpp_type_text(&combined)
14879}
14880
14881fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
14882    parameter.child_by_field_name("declarator").or_else(|| {
14883        // Some unnamed prototype parameters expose their abstract declarator
14884        // as a direct named child without the grammar's `declarator` field.
14885        // Recover only the structured abstract-declarator node; the parameter's
14886        // type and qualifiers are distinct children and must not be guessed from
14887        // source text.
14888        let mut cursor = parameter.walk();
14889        parameter
14890            .named_children(&mut cursor)
14891            .find(|child| is_cpp_abstract_declarator(child.kind()))
14892    })
14893}
14894
14895/// Whether a parameter's declarator chain adds indirection - a pointer,
14896/// reference, array or function declarator - to the parameter's written type.
14897pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
14898    let mut current = Some(declarator);
14899    while let Some(node) = current {
14900        if matches!(
14901            node.kind(),
14902            "pointer_declarator"
14903                | "abstract_pointer_declarator"
14904                | "reference_declarator"
14905                | "abstract_reference_declarator"
14906                | "array_declarator"
14907                | "abstract_array_declarator"
14908                | "function_declarator"
14909                | "abstract_function_declarator"
14910        ) {
14911            return true;
14912        }
14913        current = cpp_nested_declarator(node);
14914    }
14915    false
14916}
14917
14918fn is_cpp_abstract_declarator(kind: &str) -> bool {
14919    matches!(
14920        kind,
14921        "abstract_pointer_declarator"
14922            | "abstract_reference_declarator"
14923            | "abstract_array_declarator"
14924            | "abstract_function_declarator"
14925            | "abstract_parenthesized_declarator"
14926    )
14927}
14928
14929fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
14930    node.child_by_field_name("declarator").or_else(|| {
14931        if is_cpp_abstract_declarator(node.kind()) {
14932            let mut cursor = node.walk();
14933            node.named_children(&mut cursor)
14934                .find(|child| is_cpp_abstract_declarator(child.kind()))
14935        } else {
14936            // Named declarators historically use their last named child when
14937            // tree-sitter omits the field. Keep that broad fallback for
14938            // attributed, variadic, and recovered named shapes.
14939            last_named_child(node)
14940        }
14941    })
14942}
14943
14944fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
14945    match node.kind() {
14946        "identifier" | "field_identifier" => String::new(),
14947        "pointer_declarator" | "abstract_pointer_declarator" => {
14948            let inner = cpp_nested_declarator(node)
14949                .map(|child| cpp_declarator_suffix_without_name(child, source))
14950                .unwrap_or_default();
14951            format!("*{inner}")
14952        }
14953        "reference_declarator" | "abstract_reference_declarator" => {
14954            let inner = cpp_nested_declarator(node)
14955                .map(|child| cpp_declarator_suffix_without_name(child, source))
14956                .unwrap_or_default();
14957            let reference = node
14958                .children(&mut node.walk())
14959                .find(|child| matches!(child.kind(), "&" | "&&"))
14960                .map(|child| node_text(child, source))
14961                .unwrap_or("&");
14962            format!("{reference}{inner}")
14963        }
14964        "array_declarator" | "abstract_array_declarator" => {
14965            let inner = cpp_nested_declarator(node)
14966                .map(|child| cpp_declarator_suffix_without_name(child, source))
14967                .unwrap_or_default();
14968            let size = node
14969                .child_by_field_name("size")
14970                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
14971                .unwrap_or_default();
14972            format!("{inner}[{size}]")
14973        }
14974        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
14975            let inner = cpp_nested_declarator(node);
14976            inner
14977                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
14978                .unwrap_or_default()
14979        }
14980        "function_declarator" | "abstract_function_declarator" => {
14981            let inner = cpp_nested_declarator(node)
14982                .map(|child| cpp_declarator_suffix_without_name(child, source))
14983                .unwrap_or_default();
14984            let params = node
14985                .child_by_field_name("parameters")
14986                .map(|child| cpp_parameter_signature(child, source))
14987                .unwrap_or_else(|| "()".to_string());
14988            format!("{inner}{params}")
14989        }
14990        _ => {
14991            let text = normalize_cpp_whitespace(node_text(node, source));
14992            let name = extract_declarator_name(node, source);
14993            if name.is_empty() {
14994                text
14995            } else {
14996                text.replace(&name, "").trim().to_string()
14997            }
14998        }
14999    }
15000}
15001
15002fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
15003    collapse_cpp_whitespace(
15004        suffix
15005            .trim()
15006            .trim_start_matches("->")
15007            .trim_start_matches('{')
15008            .trim_end_matches(';'),
15009    )
15010}
15011
15012pub fn normalize_cpp_whitespace(value: &str) -> String {
15013    collapse_cpp_whitespace(value)
15014}
15015
15016fn normalize_cpp_type_text(value: &str) -> String {
15017    collapse_cpp_whitespace(value)
15018        .replace(", ", ",")
15019        .replace(" <", "<")
15020        .replace("< ", "<")
15021        .replace(" >", ">")
15022}
15023
15024fn collapse_cpp_whitespace(value: &str) -> String {
15025    let mut result = String::new();
15026    let mut prev_space = false;
15027    for ch in value.chars() {
15028        if ch.is_whitespace() {
15029            if !prev_space {
15030                result.push(' ');
15031            }
15032            prev_space = true;
15033        } else {
15034            result.push(ch);
15035            prev_space = false;
15036        }
15037    }
15038    result.trim().to_string()
15039}
15040
15041pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
15042    node_source_text(node, source)
15043}
15044
15045fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
15046    node.child_by_field_name("body").or_else(|| {
15047        let mut cursor = node.walk();
15048        node.named_children(&mut cursor).find(|child| {
15049            matches!(
15050                child.kind(),
15051                "declaration_list" | "field_declaration_list" | "enumerator_list"
15052            )
15053        })
15054    })
15055}
15056
15057/// Return a class body's actual closing brace when the parser supplied one.
15058///
15059/// A malformed namespace sentinel can leave a class node carrying unrelated
15060/// parser errors even though its own class body is complete.  `has_error()` is
15061/// therefore too coarse an admission predicate for sentinel ownership.  The
15062/// body list, however, exposes the opening and closing punctuation directly;
15063/// a real (non-missing) final `}` proves that the class did not borrow the
15064/// enclosing namespace's close.  Requiring the body to end before its parent
15065/// container also rejects a recovered node whose body swallowed that outer
15066/// boundary.
15067fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
15068    if !matches!(
15069        node.kind(),
15070        "class_specifier" | "struct_specifier" | "union_specifier"
15071    ) {
15072        return None;
15073    }
15074    let body = cpp_body_node(node)?;
15075    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
15076        return None;
15077    }
15078    let open = body.child(0)?;
15079    let close = body.child(body.child_count().checked_sub(1)?)?;
15080    if open.kind() != "{"
15081        || open.is_missing()
15082        || close.kind() != "}"
15083        || close.is_missing()
15084        || close.end_byte() != body.end_byte()
15085        || body.end_byte() > node.end_byte()
15086        || node
15087            .parent()
15088            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
15089    {
15090        return None;
15091    }
15092    Some(close)
15093}
15094
15095fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
15096    if node.kind() == "namespace_definition" {
15097        return true;
15098    }
15099    let mut cursor = node.walk();
15100    node.named_children(&mut cursor)
15101        .any(cpp_contains_namespace_definition)
15102}
15103
15104struct CppNestedNamespaceSentinel<'tree> {
15105    function: Node<'tree>,
15106    body: Node<'tree>,
15107    namespace_components: Vec<String>,
15108}
15109
15110/// Owned structural recovery metadata for a namespace-sentinel region.
15111///
15112/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
15113/// instead of the namespace/class scopes that the declaration visitor restores.
15114/// The inverted usage walk has the original CST, so it needs the same ownership
15115/// evidence without borrowing parser nodes across its file scan.  Keep this
15116/// descriptor deliberately source-range based: callers can match a reference
15117/// node by containment and then resolve its structured type spelling in the
15118/// recovered class scope.
15119#[derive(Debug, Clone)]
15120pub struct CppSentinelRecoveredOwner {
15121    pub range: Range,
15122    /// Start of the qualified owner name (`btree<P>::method`).  A leading
15123    /// return type before this byte is looked up from the namespace; parameters,
15124    /// trailing returns, and the body use the member owner scope.
15125    pub owner_name_start_byte: usize,
15126    /// Number of leading components belonging to the namespace rather than
15127    /// the qualified class owner.  A leading return type is looked up before
15128    /// every owner component, not merely before the innermost class.
15129    pub namespace_component_count: usize,
15130    pub scope_components: Vec<String>,
15131}
15132
15133#[derive(Debug, Clone)]
15134pub struct CppSentinelRecoveredClass {
15135    pub namespace_range: Range,
15136    pub namespace_scope_components: Vec<String>,
15137    pub class_range: Range,
15138    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
15139    pub scope_components: Vec<String>,
15140    /// Qualified out-of-line member definitions owned by this class.  Their
15141    /// ranges may extend beyond `class_range` when the malformed sentinel
15142    /// swallowed the namespace close and left definitions as function siblings.
15143    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
15144}
15145
15146/// Resolve the lexical scope restored for a node in a malformed
15147/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
15148/// outrank class spans, which in turn outrank the surviving namespace body.
15149/// The class ancestor suffix is recovered from the original CST so nested
15150/// members keep their complete `Outer::Inner` owner chain.
15151pub fn cpp_sentinel_recovered_scope_for_node(
15152    node: Node<'_>,
15153    source: &str,
15154    recovered_classes: &[CppSentinelRecoveredClass],
15155) -> Option<Vec<String>> {
15156    let contains =
15157        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
15158    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
15159    for recovered in recovered_classes {
15160        for owner in recovered
15161            .owner_ranges
15162            .iter()
15163            .filter(|owner| contains(owner.range))
15164        {
15165            let replace = best_owner.is_none_or(|existing| {
15166                owner.range.end_byte.saturating_sub(owner.range.start_byte)
15167                    < existing
15168                        .range
15169                        .end_byte
15170                        .saturating_sub(existing.range.start_byte)
15171            });
15172            if replace {
15173                best_owner = Some(owner);
15174            }
15175        }
15176    }
15177    if let Some(owner) = best_owner {
15178        let mut scope = owner.scope_components.clone();
15179        if node.start_byte() < owner.owner_name_start_byte {
15180            scope.truncate(owner.namespace_component_count);
15181        }
15182        return Some(scope);
15183    }
15184
15185    let class = recovered_classes
15186        .iter()
15187        .filter(|recovered| contains(recovered.class_range))
15188        .min_by_key(|recovered| {
15189            recovered
15190                .class_range
15191                .end_byte
15192                .saturating_sub(recovered.class_range.start_byte)
15193        });
15194    let class_scope = class.is_some();
15195    let mut scope = if let Some(class) = class {
15196        class.scope_components.clone()
15197    } else {
15198        let namespace = recovered_classes
15199            .iter()
15200            .filter(|recovered| contains(recovered.namespace_range))
15201            .min_by_key(|recovered| {
15202                recovered
15203                    .namespace_range
15204                    .end_byte
15205                    .saturating_sub(recovered.namespace_range.start_byte)
15206            })?;
15207        let mut scope = namespace.namespace_scope_components.clone();
15208        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
15209        let common_prefix = scope
15210            .iter()
15211            .zip(&parser_namespace)
15212            .take_while(|(recovered, parser)| recovered == parser)
15213            .count();
15214        scope.extend(parser_namespace.into_iter().skip(common_prefix));
15215        scope
15216    };
15217    if class_scope {
15218        let mut ancestor_components = Vec::new();
15219        let mut ancestor = node.parent();
15220        while let Some(current) = ancestor {
15221            if matches!(
15222                current.kind(),
15223                "class_specifier" | "struct_specifier" | "union_specifier"
15224            ) && let Some(name) = current.child_by_field_name("name")
15225                && let Some(name_components) = cpp_name_components(name, source)
15226            {
15227                ancestor_components.push(
15228                    name_components
15229                        .into_iter()
15230                        .map(|component| component.name)
15231                        .collect::<Vec<_>>(),
15232                );
15233            }
15234            ancestor = current.parent();
15235        }
15236        ancestor_components.reverse();
15237        let base_len = scope.len();
15238        for component in ancestor_components.into_iter().flatten() {
15239            if scope.len() >= base_len && scope.last() == Some(&component) {
15240                continue;
15241            }
15242            scope.push(component);
15243        }
15244    }
15245    Some(scope)
15246}
15247
15248struct CppSentinelFragmentedClassTail<'tree> {
15249    class_node: Node<'tree>,
15250    template_node: Option<Node<'tree>>,
15251    name: String,
15252    raw_supertypes: Option<Vec<String>>,
15253    fragmented: FragmentedExportBody,
15254    consumed_start: usize,
15255}
15256
15257struct CppSentinelFragmentedClassErrorPrefix<'tree> {
15258    name: String,
15259    open: Node<'tree>,
15260    raw_supertypes: Option<Vec<String>>,
15261}
15262
15263struct CppSentinelDirectBodyClassRegion {
15264    namespace_components: Vec<String>,
15265    class_start: usize,
15266    class_start_line: usize,
15267    class_close_end: usize,
15268    class_close_line: usize,
15269    name: String,
15270}
15271
15272fn cpp_sentinel_body_class_candidate<'tree>(
15273    child: Node<'tree>,
15274) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15275    if matches!(
15276        child.kind(),
15277        "class_specifier" | "struct_specifier" | "union_specifier"
15278    ) {
15279        return Some((child, None));
15280    }
15281    if child.kind() != "template_declaration" {
15282        if child.kind() == "declaration" {
15283            return Some((first_class_like_child(child)?, None));
15284        }
15285        return None;
15286    }
15287    let mut cursor = child.walk();
15288    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
15289        if matches!(
15290            candidate.kind(),
15291            "class_specifier" | "struct_specifier" | "union_specifier"
15292        ) {
15293            Some(candidate)
15294        } else if candidate.kind() == "declaration" {
15295            first_class_like_child(candidate)
15296        } else {
15297            None
15298        }
15299    })?;
15300    Some((class_node, Some(child)))
15301}
15302
15303/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
15304/// namespace-sentinel body when a later member macro ends the bogus sentinel
15305/// function before the real class close. The anonymous class/open tokens and
15306/// direct identifier are the structural proof; a retained direct close would
15307/// be an ordinary malformed class rather than the fragmented tail handled here.
15308fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
15309    node: Node<'tree>,
15310    source: &str,
15311) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
15312    let name = malformed_class_error_owner_name(node, source)?;
15313    let mut cursor = node.walk();
15314    let children = node.children(&mut cursor).collect::<Vec<_>>();
15315    let keyword = children.first()?;
15316    let open_index = children.iter().position(|child| child.kind() == "{")?;
15317    if children[open_index + 1..]
15318        .iter()
15319        .any(|child| child.kind() == "}")
15320    {
15321        return None;
15322    }
15323    let raw_supertypes =
15324        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
15325    Some(CppSentinelFragmentedClassErrorPrefix {
15326        name,
15327        open: children[open_index],
15328        raw_supertypes,
15329    })
15330}
15331
15332fn cpp_sentinel_direct_body_class_candidate<'tree>(
15333    child: Node<'tree>,
15334) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15335    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
15336        return Some(candidate);
15337    }
15338    if child.kind() != "template_declaration" {
15339        return None;
15340    }
15341    let mut cursor = child.walk();
15342    let wrapper = child
15343        .named_children(&mut cursor)
15344        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
15345    Some((first_class_like_child(wrapper)?, Some(child)))
15346}
15347
15348fn cpp_sentinel_direct_namespace_components(
15349    function: Node<'_>,
15350    body: Node<'_>,
15351    source: &str,
15352) -> Option<Vec<String>> {
15353    let mut cursor = function.walk();
15354    let children = function
15355        .named_children(&mut cursor)
15356        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
15357        .collect::<Vec<_>>();
15358    let sentinel_index = children.iter().rposition(|child| {
15359        direct_identifier_name(*child, source)
15360            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
15361    })?;
15362    let mut identifiers = Vec::new();
15363    let mut stack = children[sentinel_index + 1..]
15364        .iter()
15365        .rev()
15366        .copied()
15367        .collect::<Vec<_>>();
15368    while let Some(current) = stack.pop() {
15369        if let Some(name) = direct_identifier_name(current, source) {
15370            identifiers.push(name);
15371            continue;
15372        }
15373        let mut cursor = current.walk();
15374        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15375        stack.extend(children.into_iter().rev());
15376    }
15377    let [keyword, namespace] = identifiers.as_slice() else {
15378        return None;
15379    };
15380    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
15381        .then(|| vec![namespace.clone()])
15382}
15383
15384fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
15385    let mut sibling = class_semicolon.next_named_sibling();
15386    let namespace_close = loop {
15387        let Some(current) = sibling else {
15388            return false;
15389        };
15390        sibling = current.next_named_sibling();
15391        if current.kind() != "comment" {
15392            break current;
15393        }
15394    };
15395    if !cpp_is_stray_close_brace(namespace_close, source) {
15396        return false;
15397    }
15398    loop {
15399        let Some(current) = sibling else {
15400            return false;
15401        };
15402        sibling = current.next_named_sibling();
15403        if current.kind() == "comment" {
15404            continue;
15405        }
15406        return direct_identifier_name(current, source)
15407            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
15408    }
15409}
15410
15411fn cpp_sentinel_macro_body_class_region<'tree>(
15412    node: Node<'tree>,
15413    source: &str,
15414    ancestry: &ParentIndex<'tree>,
15415) -> Option<CppSentinelDirectBodyClassRegion> {
15416    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
15417        return None;
15418    };
15419    if node.kind() != "function_definition" || !node.has_error() {
15420        return None;
15421    }
15422    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
15423    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
15424    let mut cursor = body.walk();
15425    let candidates = body
15426        .named_children(&mut cursor)
15427        .filter_map(cpp_sentinel_direct_body_class_candidate)
15428        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
15429        .collect::<Vec<_>>();
15430    let [(class_node, template_node)] = candidates.as_slice() else {
15431        return None;
15432    };
15433    let original_body = cpp_body_node(*class_node)?;
15434    let name = class_like_name(*class_node, source, ancestry)?;
15435    if name.is_empty() || cpp_export_macro_token(&name) {
15436        return None;
15437    }
15438
15439    let mut sibling = node.next_named_sibling();
15440    let (class_close_start, class_close_end, class_close_line) = loop {
15441        let current = sibling?;
15442        let next = current.next_named_sibling();
15443        if cpp_is_stray_close_brace(current, source)
15444            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15445        {
15446            let semicolon = next.expect("checked above");
15447            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
15448                return None;
15449            }
15450            break (
15451                current.start_byte(),
15452                semicolon.end_byte(),
15453                semicolon.end_position().row + 1,
15454            );
15455        }
15456        sibling = next;
15457    };
15458    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
15459    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15460    let root = tree.root_node();
15461    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
15462    // The region reparse is its own tree, so it needs its own parent index;
15463    // the caller's index answers nothing about these nodes.
15464    let reparsed_ancestry = ParentIndex::new(root);
15465    let reparsed =
15466        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
15467    if reparsed.name != name
15468        || reparsed.declaration_node.start_byte() != class_node.start_byte()
15469        || reparsed.body.start_byte() != original_body.start_byte()
15470        || class_close_start <= reparsed.body.end_byte()
15471        || class_close_end <= class_node.end_byte()
15472    {
15473        return None;
15474    }
15475    Some(CppSentinelDirectBodyClassRegion {
15476        namespace_components,
15477        class_start: reparse_start,
15478        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
15479            node.start_position().row + 1
15480        }),
15481        class_close_end,
15482        class_close_line,
15483        name,
15484    })
15485}
15486
15487/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
15488/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
15489///
15490/// The parser puts the namespace opener and the malformed function in one root
15491/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
15492/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
15493/// the malformed function must begin with an all-caps type, then an ERROR whose
15494/// sole identifier is `namespace`, followed by the inner namespace identifier
15495/// and a compound body; and that body must contain a complete named class or a
15496/// structurally fragmented class prefix. A text reparse cannot prove any of
15497/// those ownership boundaries.
15498fn cpp_nested_namespace_sentinel<'tree>(
15499    node: Node<'tree>,
15500    source: &str,
15501    ancestry: &ParentIndex<'tree>,
15502) -> Option<CppNestedNamespaceSentinel<'tree>> {
15503    if !node.has_error() {
15504        return None;
15505    }
15506
15507    let (function, mut namespace_components) = if node.kind() == "ERROR" {
15508        let mut cursor = node.walk();
15509        let functions = node
15510            .named_children(&mut cursor)
15511            .filter(|child| child.kind() == "function_definition")
15512            .collect::<Vec<_>>();
15513        let [function] = functions.as_slice() else {
15514            return None;
15515        };
15516        if !function.has_error() {
15517            return None;
15518        }
15519        let mut cursor = node.walk();
15520        let children = node.children(&mut cursor).collect::<Vec<_>>();
15521        let function_index = children
15522            .iter()
15523            .position(|child| same_node(*child, *function))?;
15524        let [outer_keyword, outer_name, outer_open] =
15525            children.get(function_index.checked_sub(3)?..function_index)?
15526        else {
15527            return None;
15528        };
15529        if outer_keyword.kind() != "namespace"
15530            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
15531            || outer_open.kind() != "{"
15532        {
15533            return None;
15534        }
15535        (
15536            *function,
15537            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
15538        )
15539    } else if node.kind() == "function_definition" {
15540        let declaration_list = node.parent()?;
15541        let namespace = declaration_list.parent()?;
15542        if declaration_list.kind() != "declaration_list"
15543            || namespace.kind() != "namespace_definition"
15544            || namespace.child_by_field_name("body") != Some(declaration_list)
15545        {
15546            return None;
15547        }
15548        (node, Vec::new())
15549    } else {
15550        return None;
15551    };
15552
15553    let mut cursor = function.walk();
15554    let named = function
15555        .named_children(&mut cursor)
15556        .filter(|child| child.kind() != "comment")
15557        .collect::<Vec<_>>();
15558    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
15559        return None;
15560    };
15561    if first_type.kind() != "type_identifier" {
15562        return None;
15563    }
15564    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
15565    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
15566        return None;
15567    }
15568    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
15569        return None;
15570    }
15571    let inner_keyword = inner_error.named_child(0)?;
15572    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
15573        return None;
15574    }
15575    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
15576        return None;
15577    }
15578    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
15579    if inner_name.is_empty() || body.kind() != "compound_statement" {
15580        return None;
15581    }
15582    namespace_components.push(inner_name);
15583
15584    let mut cursor = body.walk();
15585    let has_complete_class = body.named_children(&mut cursor).any(|child| {
15586        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
15587            cpp_body_node(class_node).is_some()
15588                && class_like_name(class_node, source, ancestry)
15589                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
15590        })
15591    });
15592    if !has_complete_class
15593        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
15594    {
15595        return None;
15596    }
15597
15598    Some(CppNestedNamespaceSentinel {
15599        function,
15600        body: *body,
15601        namespace_components,
15602    })
15603}
15604
15605/// Recognize a namespace-begin sentinel directly beneath the translation unit.
15606///
15607/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
15608/// function whose type is the sentinel, whose declarator is the structured
15609/// qualified name `namespace::a::b`, and whose body contains the namespace
15610/// items. Declaration indexing already reparses this bounded region. The
15611/// inverse scanner retains the original tree, so recover the same namespace
15612/// components from the declarator fields for its lexical-scope metadata.
15613fn cpp_root_namespace_sentinel<'tree>(
15614    node: Node<'tree>,
15615    source: &str,
15616    ancestry: &ParentIndex<'tree>,
15617) -> Option<CppNestedNamespaceSentinel<'tree>> {
15618    if node.kind() != "function_definition"
15619        || !node.has_error()
15620        || node.parent()?.kind() != "translation_unit"
15621    {
15622        return None;
15623    }
15624    let first_type = node.child_by_field_name("type")?;
15625    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
15626    if first_type.kind() != "type_identifier"
15627        || sentinel.is_empty()
15628        || !cpp_export_macro_token(&sentinel)
15629    {
15630        return None;
15631    }
15632    let declarator = node.child_by_field_name("declarator")?;
15633    let body = node.child_by_field_name("body")?;
15634    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
15635        return None;
15636    }
15637    let mut cursor = node.walk();
15638    let named = node
15639        .named_children(&mut cursor)
15640        .filter(|child| child.kind() != "comment")
15641        .collect::<Vec<_>>();
15642    let [named_type, named_declarator, named_body] = named.as_slice() else {
15643        return None;
15644    };
15645    if !same_node(*named_type, first_type)
15646        || !same_node(*named_declarator, declarator)
15647        || !same_node(*named_body, body)
15648    {
15649        return None;
15650    }
15651    let mut declarator_components = Vec::new();
15652    let mut valid_components = true;
15653    walk_named_tree_preorder(declarator, true, |component| {
15654        if !matches!(
15655            component.kind(),
15656            "identifier" | "namespace_identifier" | "type_identifier"
15657        ) {
15658            return WalkControl::Continue;
15659        }
15660        let Some(component) = canonical_cpp_qualified_component(component, source) else {
15661            valid_components = false;
15662            return WalkControl::Break;
15663        };
15664        declarator_components.push(component.name);
15665        WalkControl::SkipChildren
15666    });
15667    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
15668        return None;
15669    }
15670    declarator_components.remove(0);
15671    let namespace_components = declarator_components;
15672    if namespace_components.is_empty()
15673        || namespace_components
15674            .iter()
15675            .any(|component| component.is_empty() || cpp_export_macro_token(component))
15676    {
15677        return None;
15678    }
15679
15680    let mut cursor = body.walk();
15681    let has_complete_class = body.named_children(&mut cursor).any(|child| {
15682        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
15683            cpp_body_node(class_node).is_some()
15684                && class_like_name(class_node, source, ancestry)
15685                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
15686        })
15687    });
15688    if !has_complete_class
15689        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
15690    {
15691        return None;
15692    }
15693
15694    Some(CppNestedNamespaceSentinel {
15695        function: node,
15696        body,
15697        namespace_components,
15698    })
15699}
15700
15701/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
15702/// malformed namespace-sentinel function.  The recovery is deliberately
15703/// structural: the class must be a direct body item, its own class node must be
15704/// erroneous and end before a unique anonymous `}` in the enclosing
15705/// declaration-list, and that namespace's next sibling must be a standalone
15706/// `;`.  The complete interior must pass the existing member-shaped reparse
15707/// gate. This avoids source brace scans and does not borrow a close from an
15708/// unrelated later declaration.
15709fn cpp_sentinel_fragmented_class_tail<'tree>(
15710    function: Node<'tree>,
15711    body: Node<'tree>,
15712    source: &str,
15713    ancestry: &ParentIndex<'tree>,
15714) -> Option<CppSentinelFragmentedClassTail<'tree>> {
15715    let mut cursor = body.walk();
15716    let candidates = body
15717        .named_children(&mut cursor)
15718        .filter_map(|child| {
15719            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
15720                let class_body = cpp_body_node(class_node)?;
15721                if !class_node.has_error() {
15722                    return None;
15723                }
15724                let name = class_like_name(class_node, source, ancestry)?;
15725                let raw_supertypes =
15726                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
15727                        .then(|| extract_cpp_supertypes(class_node, source));
15728                return Some((
15729                    class_node,
15730                    template_node,
15731                    name,
15732                    class_body,
15733                    class_body.start_byte().checked_add(1)?,
15734                    raw_supertypes,
15735                ));
15736            }
15737            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
15738            Some((
15739                child,
15740                None,
15741                prefix.name,
15742                prefix.open,
15743                prefix.open.end_byte(),
15744                prefix.raw_supertypes,
15745            ))
15746        })
15747        .collect::<Vec<_>>();
15748    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
15749        candidates.as_slice()
15750    else {
15751        return None;
15752    };
15753    if name.is_empty() || cpp_export_macro_token(name) {
15754        return None;
15755    }
15756
15757    let (close, semicolon) =
15758        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
15759
15760    let reparse_end = close.start_byte();
15761    if *reparse_start >= reparse_end {
15762        return None;
15763    }
15764    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
15765    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
15766        return None;
15767    }
15768    let class_range = Range {
15769        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
15770        end_byte: semicolon.end_byte(),
15771        start_line: template_node.map_or(class_node.start_position().row, |node| {
15772            node.start_position().row
15773        }) + 1,
15774        end_line: semicolon.end_position().row + 1,
15775    };
15776    Some(CppSentinelFragmentedClassTail {
15777        class_node: *class_node,
15778        template_node: *template_node,
15779        name: name.clone(),
15780        raw_supertypes: raw_supertypes.clone(),
15781        fragmented: FragmentedExportBody {
15782            reparse_start: *reparse_start,
15783            reparse_end,
15784            class_range,
15785        },
15786        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
15787    })
15788}
15789
15790/// Recover the class and out-of-line owner scopes from every malformed
15791/// namespace-sentinel region in `root`.
15792///
15793/// This is the shared structural counterpart to
15794/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
15795/// reuses the visitor's sentinel/class admission predicates instead of parsing
15796/// source text a second time.  The returned values own only ranges and names, so
15797/// they can be retained by an inverted usage scan after the tree borrow ends.
15798pub fn cpp_sentinel_recovered_classes(
15799    root: Node<'_>,
15800    source: &str,
15801) -> Vec<CppSentinelRecoveredClass> {
15802    if !root.has_error() {
15803        return Vec::new();
15804    }
15805    // This scan owns its walk of `root`, so it owns the parent index that walk
15806    // asks its ancestor questions through. Built after the error gate: a clean
15807    // tree returns without paying for one.
15808    let ancestry = ParentIndex::new(root);
15809    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
15810    let mut stack = vec![root];
15811    while let Some(current) = stack.pop() {
15812        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
15813            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
15814        {
15815            let namespace_components = cpp_sentinel_recovered_namespace_components(
15816                recovered.function,
15817                &recovered.namespace_components,
15818                source,
15819            );
15820            let fragmented = cpp_sentinel_fragmented_class_tail(
15821                recovered.function,
15822                recovered.body,
15823                source,
15824                &ancestry,
15825            );
15826            let mut class_candidates = Vec::new();
15827            let mut cursor = recovered.body.walk();
15828            for (class_node, template_node) in recovered
15829                .body
15830                .named_children(&mut cursor)
15831                .filter_map(cpp_sentinel_body_class_candidate)
15832            {
15833                let Some(name) = class_like_name(class_node, source, &ancestry) else {
15834                    continue;
15835                };
15836                if name.is_empty() || cpp_export_macro_token(&name) {
15837                    continue;
15838                }
15839                let is_fragmented = fragmented
15840                    .as_ref()
15841                    .is_some_and(|tail| same_node(tail.class_node, class_node));
15842                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
15843                    continue;
15844                }
15845                let class_range = if is_fragmented {
15846                    fragmented
15847                        .as_ref()
15848                        .map(|tail| tail.fragmented.class_range)
15849                        .expect("fragmented class range is present when class matches")
15850                } else {
15851                    cpp_declaration_range(template_node.unwrap_or(class_node))
15852                };
15853                class_candidates.push((class_range, name));
15854            }
15855            if let Some(fragmented) = fragmented
15856                .as_ref()
15857                .filter(|tail| tail.class_node.kind() == "ERROR")
15858            {
15859                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
15860            }
15861
15862            let mut owner_ranges =
15863                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
15864            cpp_sentinel_extend_unique_owner_ranges(
15865                &mut owner_ranges,
15866                cpp_sentinel_recovered_sibling_owner_ranges(
15867                    recovered.function,
15868                    &namespace_components,
15869                    source,
15870                ),
15871            );
15872            for (class_range, name) in class_candidates {
15873                push_cpp_sentinel_recovered_class(
15874                    &mut recovered_classes,
15875                    cpp_declaration_range(recovered.body),
15876                    &namespace_components,
15877                    class_range,
15878                    name,
15879                    &owner_ranges,
15880                );
15881            }
15882
15883            if let Some(declaration_list) = recovered
15884                .function
15885                .parent()
15886                .filter(|parent| parent.kind() == "declaration_list")
15887            {
15888                let outer_namespace =
15889                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
15890                push_cpp_sentinel_sibling_classes(
15891                    &mut recovered_classes,
15892                    declaration_list,
15893                    recovered.function,
15894                    &outer_namespace,
15895                    source,
15896                    &ancestry,
15897                );
15898            }
15899        } else if let Some(region) =
15900            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
15901        {
15902            let namespace_components = cpp_sentinel_recovered_namespace_components(
15903                current,
15904                &region.namespace_components,
15905                source,
15906            );
15907            let owner_container = current
15908                .parent()
15909                .filter(|parent| parent.kind() == "declaration_list")
15910                .unwrap_or(current);
15911            let owner_ranges =
15912                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
15913            push_cpp_sentinel_recovered_class(
15914                &mut recovered_classes,
15915                cpp_declaration_range(owner_container),
15916                &namespace_components,
15917                Range {
15918                    start_byte: region.class_start,
15919                    end_byte: region.class_close_end,
15920                    start_line: region.class_start_line,
15921                    end_line: region.class_close_line,
15922                },
15923                region.name,
15924                &owner_ranges,
15925            );
15926        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
15927            // A generic sentinel-prefixed class can be reduced as a malformed
15928            // function/ERROR without the explicit `namespace X` token pair.
15929            // Reuse the declaration visitor's bounded reparse and retain only
15930            // the recovered class identity/range here.
15931            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
15932                region;
15933            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
15934                continue;
15935            };
15936            let root = tree.root_node();
15937            let template_node = cpp_sentinel_reparsed_leading_template(root);
15938            // A region reparse is its own tree and needs its own parent index.
15939            let reparsed_ancestry = ParentIndex::new(root);
15940            let Some(reparsed_class) =
15941                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
15942            else {
15943                continue;
15944            };
15945            let class_node = reparsed_class.declaration_node;
15946            let name = reparsed_class.name;
15947            let namespace_components =
15948                cpp_sentinel_recovered_namespace_components(current, &[], source);
15949            let owner_container = current
15950                .parent()
15951                .filter(|parent| parent.kind() == "declaration_list")
15952                .unwrap_or(current);
15953            let mut owner_ranges =
15954                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
15955            cpp_sentinel_extend_unique_owner_ranges(
15956                &mut owner_ranges,
15957                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
15958            );
15959            push_cpp_sentinel_recovered_class(
15960                &mut recovered_classes,
15961                cpp_declaration_range(owner_container),
15962                &namespace_components,
15963                Range {
15964                    start_byte: class_start,
15965                    end_byte: close_end,
15966                    start_line: class_node.start_position().row + 1,
15967                    end_line: class_node.end_position().row + 1,
15968                },
15969                name,
15970                &owner_ranges,
15971            );
15972            if owner_container.kind() == "declaration_list" {
15973                push_cpp_sentinel_sibling_classes(
15974                    &mut recovered_classes,
15975                    owner_container,
15976                    current,
15977                    &namespace_components,
15978                    source,
15979                    &ancestry,
15980                );
15981            }
15982        }
15983
15984        let mut cursor = current.walk();
15985        stack.extend(current.named_children(&mut cursor));
15986    }
15987    // A shallower sentinel can expose nested classes as apparent namespace
15988    // siblings even after a deeper sentinel proves that a containing class
15989    // owns their ranges. Drop those shadow descriptors; scope recovery starts
15990    // from the proven containing class and appends parser-visible class
15991    // ancestors, preserving the full `Outer::Inner` chain.
15992    let shadowed = recovered_classes
15993        .iter()
15994        .map(|candidate| {
15995            recovered_classes.iter().any(|container| {
15996                container.class_range.start_byte <= candidate.class_range.start_byte
15997                    && container.class_range.end_byte >= candidate.class_range.end_byte
15998                    && container.class_range != candidate.class_range
15999                    && container.namespace_scope_components.len()
16000                        > candidate.namespace_scope_components.len()
16001                    && container
16002                        .namespace_scope_components
16003                        .starts_with(&candidate.namespace_scope_components)
16004            })
16005        })
16006        .collect::<Vec<_>>();
16007    let mut index = 0usize;
16008    recovered_classes.retain(|_| {
16009        let keep = !shadowed[index];
16010        index += 1;
16011        keep
16012    });
16013    recovered_classes
16014}
16015
16016/// A flat sentinel can swallow the first class while leaving later classes and
16017/// their out-of-line definitions as ordinary declaration-list siblings.  Once
16018/// the malformed class proves the sentinel envelope, retain those structurally
16019/// complete sibling classes under the same surviving namespace so every member
16020/// owner in the region uses one recovery contract.
16021fn push_cpp_sentinel_sibling_classes<'tree>(
16022    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16023    declaration_list: Node<'tree>,
16024    sentinel_node: Node<'tree>,
16025    namespace_components: &[String],
16026    source: &str,
16027    ancestry: &ParentIndex<'tree>,
16028) {
16029    let owner_ranges =
16030        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
16031    let namespace_range = cpp_declaration_range(declaration_list);
16032    let mut cursor = declaration_list.walk();
16033    for (class_node, template_node) in declaration_list
16034        .named_children(&mut cursor)
16035        .filter(|child| !same_node(*child, sentinel_node))
16036        .filter_map(cpp_sentinel_body_class_candidate)
16037    {
16038        let Some(name) = class_like_name(class_node, source, ancestry) else {
16039            continue;
16040        };
16041        if name.is_empty()
16042            || cpp_export_macro_token(&name)
16043            || cpp_complete_class_body_close(class_node).is_none()
16044        {
16045            continue;
16046        }
16047        push_cpp_sentinel_recovered_class(
16048            recovered_classes,
16049            namespace_range,
16050            namespace_components,
16051            cpp_declaration_range(template_node.unwrap_or(class_node)),
16052            name,
16053            &owner_ranges,
16054        );
16055    }
16056}
16057
16058fn push_cpp_sentinel_recovered_class(
16059    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16060    namespace_range: Range,
16061    namespace_components: &[String],
16062    class_range: Range,
16063    name: String,
16064    owner_ranges: &[CppSentinelRecoveredOwner],
16065) {
16066    let mut scope_components = namespace_components.to_vec();
16067    scope_components.push(name);
16068    let owner_ranges = owner_ranges
16069        .iter()
16070        .filter(|owner| owner.scope_components.starts_with(&scope_components))
16071        .cloned()
16072        .collect::<Vec<_>>();
16073    if recovered_classes.iter().any(|existing| {
16074        existing.class_range == class_range && existing.scope_components == scope_components
16075    }) {
16076        return;
16077    }
16078    recovered_classes.push(CppSentinelRecoveredClass {
16079        namespace_range,
16080        namespace_scope_components: namespace_components.to_vec(),
16081        class_range,
16082        scope_components,
16083        owner_ranges,
16084    });
16085}
16086
16087fn cpp_sentinel_recovered_namespace_components(
16088    function: Node<'_>,
16089    recovered_components: &[String],
16090    source: &str,
16091) -> Vec<String> {
16092    let mut ancestor_components = Vec::new();
16093    let mut ancestor = function.parent();
16094    while let Some(current) = ancestor {
16095        if current.kind() == "namespace_definition"
16096            && let Some(name_node) = current.child_by_field_name("name")
16097            && let Some(components) = cpp_name_components(name_node, source)
16098        {
16099            ancestor_components.push(
16100                components
16101                    .into_iter()
16102                    .map(|component| component.name)
16103                    .collect::<Vec<_>>(),
16104            );
16105        }
16106        ancestor = current.parent();
16107    }
16108    ancestor_components.reverse();
16109    let mut ancestors = ancestor_components
16110        .into_iter()
16111        .flatten()
16112        .collect::<Vec<_>>();
16113
16114    let overlap = (0..=ancestors.len().min(recovered_components.len()))
16115        .rev()
16116        .find(|length| {
16117            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
16118        })
16119        .unwrap_or(0);
16120    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
16121    ancestors
16122}
16123
16124fn cpp_sentinel_recovered_owner_ranges(
16125    body: Node<'_>,
16126    namespace_components: &[String],
16127    source: &str,
16128) -> Vec<CppSentinelRecoveredOwner> {
16129    let mut owners = Vec::new();
16130    walk_named_tree_preorder(body, true, |node| {
16131        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16132    });
16133    owners
16134}
16135
16136fn cpp_sentinel_collect_owner_range(
16137    node: Node<'_>,
16138    namespace_components: &[String],
16139    source: &str,
16140    owners: &mut Vec<CppSentinelRecoveredOwner>,
16141) -> WalkControl {
16142    if node.kind() != "function_definition" {
16143        return WalkControl::Continue;
16144    }
16145    let Some(function_declarator) = extract_function_declarator(node) else {
16146        return WalkControl::Continue;
16147    };
16148    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
16149        return WalkControl::Continue;
16150    };
16151    let Some(mut components) = cpp_name_components(name_node, source) else {
16152        return WalkControl::Continue;
16153    };
16154    if components.len() <= 1 {
16155        return WalkControl::Continue;
16156    }
16157    components.pop();
16158    let mut owner_components = components
16159        .into_iter()
16160        .map(|component| component.name)
16161        .collect::<Vec<_>>();
16162    let overlap = (0..=namespace_components.len().min(owner_components.len()))
16163        .rev()
16164        .find(|length| {
16165            owner_components[..*length]
16166                == namespace_components[namespace_components.len().saturating_sub(*length)..]
16167        })
16168        .unwrap_or(0);
16169    let mut scope_components = namespace_components.to_vec();
16170    scope_components.extend(owner_components.drain(overlap..));
16171    if scope_components.len() <= namespace_components.len() {
16172        return WalkControl::Continue;
16173    }
16174    let range = cpp_declaration_range(node);
16175    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
16176        existing.range == range && existing.scope_components == scope_components
16177    }) {
16178        owners.push(CppSentinelRecoveredOwner {
16179            range,
16180            owner_name_start_byte: name_node.start_byte(),
16181            namespace_component_count: namespace_components.len(),
16182            scope_components,
16183        });
16184    }
16185    WalkControl::Continue
16186}
16187
16188fn cpp_sentinel_extend_unique_owner_ranges(
16189    owners: &mut Vec<CppSentinelRecoveredOwner>,
16190    additional: Vec<CppSentinelRecoveredOwner>,
16191) {
16192    for owner in additional {
16193        if !owners.iter().any(|existing| {
16194            existing.range == owner.range && existing.scope_components == owner.scope_components
16195        }) {
16196            owners.push(owner);
16197        }
16198    }
16199}
16200
16201fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
16202    if node.kind() != "ERROR" || node.named_child_count() != 1 {
16203        return false;
16204    }
16205    let Some(end_name) = node.named_child(0) else {
16206        return false;
16207    };
16208    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
16209        return false;
16210    }
16211    let mut cursor = node.walk();
16212    node.children(&mut cursor)
16213        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
16214}
16215
16216/// Collect owner definitions that the malformed sentinel left as later
16217/// declaration-list siblings. Parser-visible namespace siblings are a hard
16218/// boundary: their declarations must keep their own lexical namespace.
16219fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16220    parent: Node<'_>,
16221    sentinel_node: Node<'_>,
16222    namespace_components: &[String],
16223    source: &str,
16224) -> Vec<CppSentinelRecoveredOwner> {
16225    let mut owners = Vec::new();
16226    let mut after_sentinel = false;
16227    let mut cursor = parent.walk();
16228    for child in parent.named_children(&mut cursor) {
16229        if !after_sentinel {
16230            if same_node(child, sentinel_node) {
16231                after_sentinel = true;
16232            }
16233            continue;
16234        }
16235        walk_named_tree_preorder(child, true, |node| {
16236            if node.kind() == "namespace_definition" {
16237                return WalkControl::SkipChildren;
16238            }
16239            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16240        });
16241    }
16242    owners
16243}
16244
16245/// Collect owner definitions after a malformed namespace, stopping only at
16246/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
16247/// enclosing container is not trusted to belong to the recovered namespace.
16248fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16249    parent: Node<'_>,
16250    sentinel_node: Node<'_>,
16251    namespace_components: &[String],
16252    source: &str,
16253) -> Option<Vec<CppSentinelRecoveredOwner>> {
16254    let mut owners = Vec::new();
16255    let mut after_namespace = false;
16256    let mut cursor = parent.walk();
16257    for child in parent.named_children(&mut cursor) {
16258        if !after_namespace {
16259            if same_node(child, sentinel_node) {
16260                after_namespace = true;
16261            }
16262            continue;
16263        }
16264        if cpp_sentinel_namespace_end(child, source) {
16265            return Some(owners);
16266        }
16267        walk_named_tree_preorder(child, true, |node| {
16268            if node.kind() == "namespace_definition" {
16269                return WalkControl::SkipChildren;
16270            }
16271            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16272        });
16273    }
16274    None
16275}
16276
16277fn cpp_sentinel_recovered_sibling_owner_ranges(
16278    sentinel_node: Node<'_>,
16279    namespace_components: &[String],
16280    source: &str,
16281) -> Vec<CppSentinelRecoveredOwner> {
16282    let Some(declaration_list) = sentinel_node
16283        .parent()
16284        .filter(|parent| parent.kind() == "declaration_list")
16285    else {
16286        return Vec::new();
16287    };
16288    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16289        declaration_list,
16290        sentinel_node,
16291        namespace_components,
16292        source,
16293    );
16294
16295    let Some(namespace) = declaration_list
16296        .parent()
16297        .filter(|parent| parent.kind() == "namespace_definition")
16298    else {
16299        return owners;
16300    };
16301    let Some(outer_parent) = namespace.parent() else {
16302        return owners;
16303    };
16304    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16305        outer_parent,
16306        namespace,
16307        namespace_components,
16308        source,
16309    ) {
16310        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
16311    }
16312    owners
16313}
16314
16315fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
16316    let mut current = function_declarator.child_by_field_name("declarator")?;
16317    loop {
16318        if let Some(name) = macro_decorated_unqualified_name(current) {
16319            current = name;
16320            continue;
16321        }
16322        if matches!(
16323            current.kind(),
16324            "qualified_identifier"
16325                | "scoped_identifier"
16326                | "scoped_type_identifier"
16327                | "identifier"
16328                | "field_identifier"
16329                | "operator_name"
16330                | "destructor_name"
16331                | "literal_operator_name"
16332        ) {
16333            return Some(current);
16334        }
16335        current = current
16336            .child_by_field_name("declarator")
16337            .or_else(|| current.child_by_field_name("name"))
16338            .or_else(|| last_named_child(current))?;
16339    }
16340}
16341
16342/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
16343/// `::` between the halves. The scope is an attribute-like macro token, not
16344/// a namespace; `Name` is the declared name.
16345///
16346/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
16347/// tree-sitter joins the attribute macro and the constructor name into one
16348/// `qualified_identifier` and marks the separator it had to invent as MISSING.
16349/// That flag is the grammar's own record that no separator is in the source,
16350/// so read it instead of looking for `::` in the node text. A genuine
16351/// `Outer::Inner` keeps a present separator and is declined here, and so is
16352/// the explicit-global `::name`, whose `::` is present and whose scope is
16353/// absent.
16354fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
16355    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
16356        return None;
16357    }
16358    let mut cursor = node.walk();
16359    if node
16360        .children(&mut cursor)
16361        .any(|child| child.kind() == "::" && !child.is_missing())
16362    {
16363        return None;
16364    }
16365    node.child_by_field_name("name")
16366}
16367
16368fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
16369    if let Some(name) = macro_decorated_unqualified_name(node) {
16370        return cpp_name_components(name, source);
16371    }
16372    match node.kind() {
16373        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16374            let mut components = match node.child_by_field_name("scope") {
16375                Some(scope) => cpp_name_components(scope, source)?,
16376                None => Vec::new(),
16377            };
16378            let name = node.child_by_field_name("name")?;
16379            components.push(canonical_cpp_qualified_component(name, source)?);
16380            Some(components)
16381        }
16382        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
16383    }
16384}
16385
16386fn cpp_sentinel_fragment_boundary<'tree>(
16387    function: Node<'tree>,
16388    class_node: Node<'tree>,
16389    class_body: Node<'tree>,
16390    source: &str,
16391) -> Option<(Node<'tree>, Node<'tree>)> {
16392    let declaration_list = function.parent()?;
16393    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
16394        return None;
16395    }
16396    let namespace = declaration_list.parent()?;
16397    if namespace.kind() != "namespace_definition"
16398        || namespace.child_by_field_name("body") != Some(declaration_list)
16399    {
16400        return None;
16401    }
16402    let mut cursor = declaration_list.walk();
16403    let closes = declaration_list
16404        .children(&mut cursor)
16405        .filter(|child| {
16406            !child.is_named()
16407                && child.kind() == "}"
16408                && child.start_byte() >= function.end_byte()
16409                && child.start_byte() > class_node.end_byte()
16410                && child.start_byte() > class_body.start_byte()
16411        })
16412        .collect::<Vec<_>>();
16413    let [close] = closes.as_slice() else {
16414        return None;
16415    };
16416    let semicolon = namespace.next_named_sibling()?;
16417    if !cpp_is_stray_semicolon(semicolon, source)
16418        || close.end_byte() != namespace.end_byte()
16419        || semicolon.start_byte() < namespace.end_byte()
16420    {
16421        return None;
16422    }
16423    Some((*close, semicolon))
16424}
16425
16426/// Detect the bogus declaration/function tree that tree-sitter recovers for a
16427/// region prefixed by an object-like macro sentinel the parser cannot see
16428/// (issue #941), and return the byte range `[start, end)` of the swallowed
16429/// declaration interior to reparse.
16430///
16431/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
16432/// `function_definition` whose first non-comment named child is the sentinel
16433/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
16434/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
16435/// the real items.
16436/// `start` is the end of the sentinel identifier -- everything after it is the
16437/// genuine source. `end` is the node's end, extended across any trailing empty
16438/// `;` statement the mis-parse displaced past the node (the class/struct closing
16439/// semicolon), so the reparse sees a complete, brace-balanced item.
16440///
16441/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
16442/// node (`has_error`). Unknown annotation/export macros can make a real callable
16443/// error-recovered even though tree-sitter still preserves its declarator, so a
16444/// preserved callable is admitted only when a displaced class keyword precedes
16445/// that declarator. The clean-reparse-to-items gate in
16446/// `cpp_reparsed_items_are_indexable` is the final arbiter.
16447/// Return the reparse start and, when present, the structurally recovered class
16448/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
16449/// separately from the reparse start because an opaque template-declaration
16450/// macro may precede it.
16451fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
16452    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
16453    {
16454        return None;
16455    }
16456    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
16457    // retain a valid function declarator despite the unknown export macro making
16458    // the outer node erroneous. Remember that declarator for the ordering gate
16459    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
16460    // class keyword precedes a spurious callable assembled from a later member.
16461    let mut declarator_cursor = node.walk();
16462    let preserved_callable = node
16463        .children_by_field_name("declarator", &mut declarator_cursor)
16464        .find_map(extract_function_declarator);
16465    // Leading documentation comments are attached to the malformed
16466    // `function_definition` as named children.  They are not part of the
16467    // sentinel prefix, so select the first non-comment child structurally
16468    // rather than requiring the sentinel to be child zero.  This is the shape
16469    // emitted for nlohmann/json's `basic_json`: its class documentation comment
16470    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
16471    // envelope otherwise ends at the first nested union.
16472    let mut cursor = node.walk();
16473    let first = node
16474        .named_children(&mut cursor)
16475        .find(|child| child.kind() != "comment")?;
16476    if first.kind() != "type_identifier" {
16477        return None;
16478    }
16479    let sentinel = normalize_cpp_whitespace(node_text(first, source));
16480    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16481        return None;
16482    }
16483    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
16484    // makes the trailing sentinel of one region and the leading sentinel of the
16485    // next both land as bare macro-token identifiers ahead of the real content.
16486    // Advance past every leading macro-token identifier so the reparse begins at
16487    // genuine source rather than another sentinel that would re-form the bogus
16488    // shape and fail the reparse gate.
16489    let mut start = first.end_byte();
16490    let mut after_first = false;
16491    let mut cursor = node.walk();
16492    for child in node.named_children(&mut cursor) {
16493        if !after_first {
16494            if same_node(child, first) {
16495                after_first = true;
16496            }
16497            continue;
16498        }
16499        if matches!(child.kind(), "identifier" | "type_identifier")
16500            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
16501        {
16502            start = child.end_byte();
16503        } else {
16504            break;
16505        }
16506    }
16507    // An additional opaque template-declaration macro before a class can be
16508    // folded into the bogus function's qualified declarator.  In that shape
16509    // the macro is not a direct sibling we can skip above; tree-sitter exposes
16510    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
16511    // Reparse from that keyword (or a real preceding `template` keyword) so the
16512    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
16513    // a class nested in a genuine sentinel-wrapped namespace lies after the
16514    // body opening and must not change the established region start.
16515    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
16516    let mut class_start = None;
16517    let mut template_start = None;
16518    let mut stack = vec![node];
16519    while let Some(current) = stack.pop() {
16520        if current.start_byte() >= prefix_end {
16521            continue;
16522        }
16523        if matches!(
16524            current.kind(),
16525            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
16526        ) {
16527            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
16528                "class" | "struct" | "union" | "enum" => {
16529                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
16530                        seen.min(current.start_byte())
16531                    }));
16532                }
16533                "template" => {
16534                    template_start =
16535                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
16536                            seen.min(current.start_byte())
16537                        }));
16538                }
16539                _ => {}
16540            }
16541        }
16542        let mut cursor = current.walk();
16543        stack.extend(current.children(&mut cursor));
16544    }
16545    if preserved_callable.is_some_and(|callable| {
16546        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
16547    }) {
16548        return None;
16549    }
16550    if let Some(class_start) = class_start {
16551        start = template_start
16552            .filter(|template_start| *template_start < class_start)
16553            .unwrap_or(class_start);
16554    }
16555    Some((start, class_start))
16556}
16557
16558/// Locate a sentinel-prefixed class whose malformed declaration was split across
16559/// root-level siblings. The true class close is represented structurally as a
16560/// lone `}` error followed by the class's displaced `;`; nested method/body
16561/// errors are not direct siblings of the sentinel node and therefore cannot
16562/// satisfy this pair.
16563fn cpp_sentinel_macro_class_region<'tree>(
16564    node: Node<'tree>,
16565    source: &str,
16566) -> Option<(usize, usize, usize, usize, usize, usize)> {
16567    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
16568        return None;
16569    };
16570    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
16571        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
16572        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
16573    if class_start >= body_open_start {
16574        return None;
16575    }
16576    let sibling_close = {
16577        let mut sibling = node.next_named_sibling();
16578        let mut found = None;
16579        while let Some(current) = sibling {
16580            let next = current.next_named_sibling();
16581            if cpp_is_stray_close_brace(current, source)
16582                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
16583            {
16584                let semicolon = next.expect("checked above");
16585                found = Some((
16586                    current.start_byte(),
16587                    semicolon.end_byte(),
16588                    semicolon.end_position().row + 1,
16589                ));
16590                break;
16591            }
16592            sibling = next;
16593        }
16594        found
16595    };
16596    // A stray `};` sibling is this class's close only when the bounded reparse
16597    // agrees the first body-bearing class ENDS there. When the malformed
16598    // envelope swallowed the class's true close, the scan can promote a much
16599    // later scope's close instead -- in protobuf-generated headers
16600    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
16601    // `struct TableStruct_*` paired with the first message class's `};`, making
16602    // the recovered "class body" span whole `namespace {}` blocks and minting
16603    // namespace-scope classes as nested members of the recovered class, which
16604    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
16605    // (#2275). On disagreement, fall through to the suffix-reparse boundary
16606    // below, which derives the close from the class node's own balanced body
16607    // range.
16608    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
16609        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
16610            return false;
16611        };
16612        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
16613        // A region reparse is its own tree and needs its own parent index.
16614        let reparsed_ancestry = ParentIndex::new(tree.root_node());
16615        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
16616            tree.root_node(),
16617            template_node,
16618            source,
16619            &reparsed_ancestry,
16620        ) else {
16621            return false;
16622        };
16623        let body = reparsed_class.body;
16624        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
16625    });
16626    let (class_close_start, class_close_end, class_close_line) =
16627        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
16628            (class_close_start, class_close_end, class_close_line)
16629        } else {
16630            // When the malformed envelope itself is an ERROR, tree-sitter can
16631            // leave the class's balanced close in the source while promoting
16632            // all following members to siblings. Reparse the complete suffix
16633            // and use the first body-bearing class node's own field range as
16634            // the partition boundary. This keeps balancing in tree-sitter and
16635            // preserves the source's original byte offsets.
16636            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
16637            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
16638            // A region reparse is its own tree and needs its own parent index.
16639            let reparsed_ancestry = ParentIndex::new(tree.root_node());
16640            let reparsed_class = cpp_sentinel_reparsed_class(
16641                tree.root_node(),
16642                template_node,
16643                source,
16644                &reparsed_ancestry,
16645            )?;
16646            let body = reparsed_class.body;
16647            let class_close_end = body.end_byte();
16648            let class_close_start = class_close_end.checked_sub(1)?;
16649            let class_close_line = body.end_position().row + 1;
16650            (class_close_start, class_close_end, class_close_line)
16651        };
16652    if class_close_start <= class_start {
16653        return None;
16654    }
16655
16656    // Reparse only far enough to expose the class body opening. This is a
16657    // structured check that the candidate really begins with a body-bearing
16658    // class-like item; the original malformed tree cannot provide that node.
16659    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
16660    let class_root = tree.root_node();
16661    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
16662    // A region reparse is its own tree and needs its own parent index.
16663    let reparsed_ancestry = ParentIndex::new(class_root);
16664    let reparsed_class =
16665        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
16666    let body = reparsed_class.body;
16667    // The class body opening must agree with the malformed wrapper's structured
16668    // body field. This rejects an inner nested class while permitting later
16669    // members to remain fragmented as root-level siblings in the bounded parse.
16670    if body.start_byte() != body_open_start {
16671        return None;
16672    }
16673    let body_start = body.start_byte().checked_add(1)?;
16674    (body_start < class_close_start).then_some((
16675        reparse_start,
16676        class_start,
16677        body_start,
16678        class_close_start,
16679        class_close_end,
16680        class_close_line,
16681    ))
16682}
16683
16684/// Find the `{` token immediately following the class/struct/union/enum token
16685/// at `class_start` in the malformed tree. The token is anonymous in the C++
16686/// grammar, so this deliberately walks all children (not only named children)
16687/// and relies on sibling structure rather than source-text searching.
16688fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
16689    let mut stack = vec![node];
16690    while let Some(current) = stack.pop() {
16691        if current.start_byte() == class_start
16692            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
16693        {
16694            let mut sibling = current.next_sibling();
16695            while let Some(candidate) = sibling {
16696                if candidate.kind() == "{" {
16697                    return Some(candidate.start_byte());
16698                }
16699                sibling = candidate.next_sibling();
16700            }
16701        }
16702        let mut cursor = current.walk();
16703        stack.extend(current.children(&mut cursor));
16704    }
16705    None
16706}
16707
16708/// The class body that tree-sitter displaced out of a sentinel-prefixed
16709/// declaration and left as the malformed node's next sibling.
16710///
16711/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
16712/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
16713/// the last child of that `ERROR` and its `{` opens a sibling
16714/// `compound_statement` instead. The body is still the malformed tree's own
16715/// structured token, which is what the caller's `body.start_byte() !=
16716/// body_open_start` agreement check needs; it just is not reachable by walking
16717/// forward from the class token inside the node.
16718fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
16719    node.next_named_sibling()
16720        .filter(|sibling| sibling.kind() == "compound_statement")
16721}
16722
16723fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
16724    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
16725    let mut end = if class_start.is_some() {
16726        cpp_macro_prefixed_class_end(source, start)?
16727    } else {
16728        node.end_byte()
16729    };
16730    if class_start.is_none()
16731        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
16732    {
16733        end = end.max(namespace_end);
16734    }
16735    let mut sibling = node.next_named_sibling();
16736    while let Some(current) = sibling {
16737        if !cpp_is_stray_semicolon(current, source) {
16738            break;
16739        }
16740        end = current.end_byte();
16741        sibling = current.next_named_sibling();
16742    }
16743    (start < end).then_some((start, end))
16744}
16745
16746/// Extend a sentinel reparse through a following namespace that tree-sitter
16747/// flattened into the sentinel node's sibling list.
16748///
16749/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
16750/// unknown macro becomes a false function return type and consumes the first
16751/// namespace body. A second `namespace detail` then loses its enclosing node:
16752/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
16753/// attaches its declarations to the surrounding error tree. Reparse from that
16754/// structured keyword so tree-sitter, rather than a source-text brace scan,
16755/// supplies the complete namespace boundary.
16756fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
16757    let mut sibling = node.next_sibling();
16758    let keyword = loop {
16759        let candidate = sibling?;
16760        sibling = candidate.next_sibling();
16761        if candidate.kind() != "comment" {
16762            break candidate;
16763        }
16764    };
16765    if keyword.kind() != "namespace" {
16766        return None;
16767    }
16768    let name = loop {
16769        let candidate = sibling?;
16770        sibling = candidate.next_sibling();
16771        if candidate.kind() != "comment" {
16772            break candidate;
16773        }
16774    };
16775    if cpp_namespace_name_components(name, source).is_empty() {
16776        return None;
16777    }
16778    let open = loop {
16779        let candidate = sibling?;
16780        sibling = candidate.next_sibling();
16781        if candidate.kind() != "comment" {
16782            break candidate;
16783        }
16784    };
16785    if open.kind() != "{" {
16786        return None;
16787    }
16788
16789    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
16790    let root = tree.root_node();
16791    let mut cursor = root.walk();
16792    let namespace = root
16793        .named_children(&mut cursor)
16794        .find(|candidate| candidate.kind() != "comment")?;
16795    (namespace.kind() == "namespace_definition"
16796        && namespace.start_byte() == keyword.start_byte()
16797        && namespace.child_by_field_name("body").is_some())
16798    .then_some(namespace.end_byte())
16799}
16800
16801/// Parse the source suffix beginning at a structurally recovered class/template
16802/// keyword and return the end of its first body-bearing class item.  The parser,
16803/// rather than a brace scanner, owns nested-body balancing.  This is needed when
16804/// the original error tree truncates the class and scatters later members as
16805/// top-level siblings.
16806fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
16807    let tree = cpp_reparse_region_items(source, start, source.len())?;
16808    let root = tree.root_node();
16809    let mut cursor = root.walk();
16810    for item in root.named_children(&mut cursor) {
16811        if item.end_byte() <= start || item.kind() == "comment" {
16812            continue;
16813        }
16814        let mut stack = vec![item];
16815        while let Some(current) = stack.pop() {
16816            if matches!(
16817                current.kind(),
16818                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
16819            ) && cpp_body_node(current).is_some()
16820            {
16821                return Some(current.end_byte());
16822            }
16823            let mut cursor = current.walk();
16824            stack.extend(current.named_children(&mut cursor));
16825        }
16826        // The recovered prefix is required to begin with the class item.  If
16827        // the first real item is something else, fail closed rather than skip
16828        // arbitrary source looking for a later class.
16829        return None;
16830    }
16831    None
16832}
16833
16834/// An empty `;` statement: the displaced closing semicolon of a struct/class that
16835/// the sentinel mis-parse split off past the bogus function node.
16836fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
16837    node.kind() == "expression_statement"
16838        && node.named_child_count() == 0
16839        && node_text(node, source).trim() == ";"
16840}
16841
16842/// Recover the member that follows CPython's object header macro when
16843/// tree-sitter folds both declarations into one malformed field.
16844///
16845/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
16846/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
16847/// sole child is the real field declarator. The tail may wrap that identifier
16848/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
16849/// malformed wrappers do not provide the same evidence that this is the
16850/// CPython macro boundary.
16851#[derive(Clone, Copy)]
16852pub(crate) struct RecoveredPyObjectHeadField<'tree> {
16853    pub(crate) type_node: Node<'tree>,
16854    pub(crate) name: Node<'tree>,
16855    pub(crate) declarator: Node<'tree>,
16856}
16857
16858impl RecoveredPyObjectHeadField<'_> {
16859    pub(crate) fn pointer_depth(self) -> i32 {
16860        let mut depth = 0;
16861        let mut current = self.declarator;
16862        while current != self.name {
16863            debug_assert_eq!(current.kind(), "pointer_declarator");
16864            depth += 1;
16865            current = current
16866                .child_by_field_name("declarator")
16867                .expect("recovered PyObject field pointer has an inner declarator");
16868        }
16869        depth
16870    }
16871}
16872
16873pub(crate) fn recovered_pyobject_head_field<'tree>(
16874    node: Node<'tree>,
16875    source: &str,
16876) -> Option<RecoveredPyObjectHeadField<'tree>> {
16877    if node.kind() != "field_declaration" {
16878        return None;
16879    }
16880    let type_node = node.child_by_field_name("type")?;
16881    if type_node.kind() != "type_identifier"
16882        || node_text(type_node, source).trim() != "PyObject_HEAD"
16883    {
16884        return None;
16885    }
16886    let pseudo_declarator = node.child_by_field_name("declarator")?;
16887    let mut cursor = node.walk();
16888    let errors = node
16889        .named_children(&mut cursor)
16890        .filter(|child| child.kind() == "ERROR")
16891        .collect::<Vec<_>>();
16892    let [error] = errors.as_slice() else {
16893        return None;
16894    };
16895    if error.named_child_count() != 1 {
16896        return None;
16897    }
16898    let error_child = error.named_child(0)?;
16899    if pseudo_declarator.kind() == "field_identifier"
16900        && error.start_byte() >= pseudo_declarator.end_byte()
16901        && error_child.kind() == "identifier"
16902    {
16903        return Some(RecoveredPyObjectHeadField {
16904            type_node: pseudo_declarator,
16905            name: error_child,
16906            declarator: error_child,
16907        });
16908    }
16909    if pseudo_declarator.kind() != "pointer_declarator"
16910        || error.end_byte() > pseudo_declarator.start_byte()
16911        || error_child.kind() != "identifier"
16912    {
16913        return None;
16914    }
16915    let mut name = pseudo_declarator;
16916    while name.kind() == "pointer_declarator" {
16917        name = name.child_by_field_name("declarator")?;
16918    }
16919    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
16920        type_node: error_child,
16921        name,
16922        declarator: pseudo_declarator,
16923    })
16924}
16925
16926/// Recover the real field name when a leading object-like annotation macro
16927/// displaces a qualified type into tree-sitter's bit-field recovery shape.
16928///
16929/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
16930/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
16931/// `bitfield_clause` containing an error plus an assignment.  The assignment's
16932/// left field is the only structured declaration name in that malformed tail.
16933/// A real bit-field is excluded by the all-caps macro type and required error.
16934fn recovered_macro_qualified_field_declarators<'tree>(
16935    node: Node<'tree>,
16936    source: &str,
16937) -> Option<Vec<Node<'tree>>> {
16938    if node.kind() != "field_declaration" {
16939        return None;
16940    }
16941    let macro_type = node.child_by_field_name("type")?;
16942    if macro_type.kind() != "type_identifier"
16943        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
16944    {
16945        return None;
16946    }
16947    let pseudo_declarator = node.child_by_field_name("declarator")?;
16948    if pseudo_declarator.kind() != "field_identifier" {
16949        return None;
16950    }
16951    let mut cursor = node.walk();
16952    let clause = node
16953        .named_children(&mut cursor)
16954        .find(|child| child.kind() == "bitfield_clause")?;
16955    if !(0..clause.named_child_count()).any(|index| {
16956        clause
16957            .named_child(index)
16958            .is_some_and(|child| child.kind() == "ERROR")
16959    }) {
16960        return None;
16961    }
16962    let mut recovered = Vec::new();
16963    let mut stack = vec![clause];
16964    while let Some(current) = stack.pop() {
16965        if current.kind() == "assignment_expression"
16966            && let Some(left) = current.child_by_field_name("left")
16967            && extract_variable_name(left, source).is_some()
16968        {
16969            recovered.push(left);
16970            break;
16971        }
16972        let mut cursor = current.walk();
16973        stack.extend(current.named_children(&mut cursor));
16974    }
16975    if recovered.is_empty() {
16976        return None;
16977    }
16978    let mut cursor = node.walk();
16979    recovered.extend(
16980        node.children_by_field_name("declarator", &mut cursor)
16981            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
16982    );
16983    Some(recovered)
16984}
16985
16986/// Recover a macro-qualified constructor that tree-sitter represents as one
16987/// field declaration. The constructor call remains inside the direct recovery
16988/// error, while each member initializer becomes a false function declarator.
16989/// The class owner proves the constructor name and lets the caller ignore those
16990/// initializer declarators.
16991fn recovered_macro_qualified_constructor_call<'tree>(
16992    node: Node<'tree>,
16993    class_name: &str,
16994    source: &str,
16995) -> Option<Node<'tree>> {
16996    if node.kind() != "field_declaration" {
16997        return None;
16998    }
16999    let macro_type = node.child_by_field_name("type")?;
17000    if macro_type.kind() != "type_identifier"
17001        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17002    {
17003        return None;
17004    }
17005    let mut cursor = node.walk();
17006    let bitfield = node
17007        .named_children(&mut cursor)
17008        .find(|child| child.kind() == "bitfield_clause")?;
17009    let error = bitfield
17010        .named_child(0)
17011        .filter(|child| child.kind() == "ERROR")?;
17012    let mut stack = vec![error];
17013    while let Some(current) = stack.pop() {
17014        if current.kind() == "call_expression"
17015            && current
17016                .child_by_field_name("function")
17017                .is_some_and(|function| node_text(function, source) == class_name)
17018            && current
17019                .child_by_field_name("arguments")
17020                .is_some_and(|arguments| arguments.kind() == "argument_list")
17021        {
17022            return Some(current);
17023        }
17024        let mut cursor = current.walk();
17025        stack.extend(current.named_children(&mut cursor));
17026    }
17027    None
17028}
17029
17030/// Recover a macro-qualified member function declaration that tree-sitter
17031/// represents as a pseudo-field. An object-like export macro before a qualified
17032/// return type can displace the namespace and type into an ERROR/bitfield
17033/// recovery, leaving the callable as a structured `call_expression`.
17034///
17035/// The caller must route this shape before ordinary declarator classification;
17036/// otherwise the displaced namespace identifier is published as a field.
17037fn recovered_macro_qualified_function_call<'tree>(
17038    node: Node<'tree>,
17039    source: &str,
17040) -> Option<Node<'tree>> {
17041    if node.kind() != "field_declaration" {
17042        return None;
17043    }
17044    let macro_type = node.child_by_field_name("type")?;
17045    if macro_type.kind() != "type_identifier"
17046        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17047    {
17048        return None;
17049    }
17050    let declarator = node.child_by_field_name("declarator")?;
17051    if declarator.kind() != "field_identifier" {
17052        return None;
17053    }
17054    let mut cursor = node.walk();
17055    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17056    if !named.iter().any(|child| {
17057        child.kind() == "storage_class_specifier"
17058            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
17059    }) {
17060        return None;
17061    }
17062    let bitfield = named
17063        .iter()
17064        .find(|child| child.kind() == "bitfield_clause")?;
17065    let mut bitfield_cursor = bitfield.walk();
17066    let payload = bitfield
17067        .named_children(&mut bitfield_cursor)
17068        .collect::<Vec<_>>();
17069    let [displaced_error, call] = payload.as_slice() else {
17070        return None;
17071    };
17072    if displaced_error.kind() != "ERROR"
17073        || displaced_error.named_child_count() != 1
17074        || displaced_error
17075            .named_child(0)
17076            .is_none_or(|child| child.kind() != "identifier")
17077        || call.kind() != "call_expression"
17078        || call
17079            .child_by_field_name("function")
17080            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
17081        || call
17082            .child_by_field_name("arguments")
17083            .is_none_or(|arguments| arguments.kind() != "argument_list")
17084    {
17085        return None;
17086    }
17087    Some(*call)
17088}
17089
17090fn recovered_macro_qualified_function_parameters(
17091    arguments: Node<'_>,
17092    source: &str,
17093) -> Option<(String, Vec<String>)> {
17094    if arguments.kind() != "argument_list" {
17095        return None;
17096    }
17097    let mut cursor = arguments.walk();
17098    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
17099    if named.is_empty() {
17100        return Some(("()".to_string(), Vec::new()));
17101    }
17102    let mut types = Vec::new();
17103    let mut labels = Vec::new();
17104    let mut index = 0;
17105    while index < named.len() {
17106        let parameter_type = named[index];
17107        let parameter_name = named.get(index + 1).copied()?;
17108        if !matches!(
17109            parameter_type.kind(),
17110            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
17111        ) || parameter_name.kind() != "ERROR"
17112            || parameter_name.named_child_count() != 1
17113            || parameter_name
17114                .named_child(0)
17115                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
17116        {
17117            return None;
17118        }
17119        let parameter_name = parameter_name.named_child(0)?;
17120        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
17121        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
17122        index += 2;
17123    }
17124    Some((format!("({})", types.join(", ")), labels))
17125}
17126
17127/// Recognize the phantom field tree-sitter emits for a macro-qualified
17128/// function return type.  For example,
17129/// `static API result_type ThresholdForSmallA() { ... }` can become a
17130/// `field_declaration` (`API` as the type and `result_type` as a field name)
17131/// followed by a clean `function_definition` for `ThresholdForSmallA`.
17132///
17133/// Keep this predicate entirely tied to the CST envelope: the type must be an
17134/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
17135/// the declaration must carry a missing semicolon rather than a real one, and
17136/// the immediate named sibling must expose a function declarator.  A real
17137/// macro-decorated field with an explicit semicolon therefore remains a field.
17138pub fn recovered_macro_return_type_node<'tree>(
17139    node: Node<'tree>,
17140    source: &str,
17141) -> Option<Node<'tree>> {
17142    if node.kind() != "field_declaration" {
17143        return None;
17144    }
17145    let macro_type = node.child_by_field_name("type")?;
17146    if macro_type.kind() != "type_identifier"
17147        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17148    {
17149        return None;
17150    }
17151    let declarator = node.child_by_field_name("declarator")?;
17152    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
17153        return None;
17154    }
17155    let mut has_missing_semicolon = false;
17156    let mut has_real_semicolon = false;
17157    for child in children_iter(node) {
17158        if child.kind() != ";" {
17159            continue;
17160        }
17161        if child.is_missing() {
17162            has_missing_semicolon = true;
17163        } else {
17164            has_real_semicolon = true;
17165        }
17166    }
17167    if !has_missing_semicolon || has_real_semicolon {
17168        return None;
17169    }
17170    let mut next = node.next_named_sibling();
17171    while next.is_some_and(|sibling| sibling.kind() == "comment") {
17172        next = next.and_then(|sibling| sibling.next_named_sibling());
17173    }
17174    let next = next?;
17175    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
17176        return None;
17177    }
17178    let function_declarator = next.child_by_field_name("declarator")?;
17179    extract_function_declarator(function_declarator).map(|_| declarator)
17180}
17181
17182/// Whether `name` is a type parameter of a template declaration that lexically
17183/// encloses `node`. The malformed macro-return field uses the parameter name as
17184/// its pseudo-declarator; preserving that field is necessary to publish a
17185/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
17186/// ancestors instead of interpreting source text so nested templates and
17187/// parser-recovered regions retain their real lexical scopes.
17188pub(crate) fn cpp_active_template_type_parameter<'tree>(
17189    node: Node<'tree>,
17190    name: &str,
17191    source: &str,
17192    ancestry: &ParentIndex<'tree>,
17193) -> bool {
17194    let mut ancestor = ancestry.parent(node);
17195    while let Some(current) = ancestor {
17196        if current.kind() == "template_declaration"
17197            && let Some(parameters) = current.child_by_field_name("parameters")
17198        {
17199            let mut cursor = parameters.walk();
17200            if parameters.named_children(&mut cursor).any(|parameter| {
17201                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
17202                    && cpp_template_parameter_name(parameter, source)
17203                        .is_some_and(|parameter_name| parameter_name == name)
17204            }) {
17205                return true;
17206            }
17207        }
17208        ancestor = ancestry.parent(current);
17209    }
17210    false
17211}
17212
17213/// Reparse the region `[start, end)` of `source` as C++, confined to the region
17214/// via included ranges so every reparsed node keeps its original byte offset and
17215/// line number. The existing visitors read node text from the original source,
17216/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
17217/// `parse_rust_region_tree` technique.
17218fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
17219    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
17220}
17221
17222fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
17223    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
17224        return None;
17225    }
17226    let semicolon = node.next_sibling()?;
17227    if semicolon.kind() != ";" || semicolon.is_missing() {
17228        return None;
17229    }
17230    let row = node.start_position().row;
17231    let mut start = node.start_byte();
17232    let mut sibling = node.prev_sibling();
17233    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
17234        if previous.kind() == ";" {
17235            break;
17236        }
17237        start = previous.start_byte();
17238        sibling = previous.prev_sibling();
17239    }
17240    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
17241}
17242
17243/// One `RET name _(( args ));`-style K&R prototype-macro invocation
17244/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
17245/// byte spans a clean reparse needs, in original-source order.
17246struct PrototypeMacroCandidate {
17247    /// Where the return type and declared name begin.
17248    run_start: usize,
17249    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
17250    /// exclusive end of the first reparse span.
17251    identifier_start: usize,
17252    /// The inner `(`'s start byte: the start of the second reparse span.
17253    /// This paren is kept so the reparse sees exactly one parameter-list
17254    /// paren pair.
17255    inner_open_start: usize,
17256    /// Byte just past the inner `)`: the end of the second reparse span.
17257    inner_close_end: usize,
17258    /// Byte just past the outer `)`: the start of the third reparse span.
17259    outer_close_end: usize,
17260    /// Byte just past the terminating `;`: the end of the third reparse
17261    /// span, and of the whole candidate.
17262    semicolon_end: usize,
17263}
17264
17265impl PrototypeMacroCandidate {
17266    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
17267    /// one included-range tree: the return type and name, the parenthesized
17268    /// argument list, and the terminating `;`. The macro token and its outer
17269    /// wrapping parenthesis are omitted -- deleting them is exactly what the
17270    /// (absent) preprocessor macro expansion would do.
17271    fn ranges(&self) -> [(usize, usize); 3] {
17272        [
17273            (self.run_start, self.identifier_start),
17274            (self.inner_open_start, self.inner_close_end),
17275            (self.outer_close_end, self.semicolon_end),
17276        ]
17277    }
17278}
17279
17280/// A live terminating semicolon owned directly by `node`.
17281fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
17282    node.child(node.child_count().checked_sub(1)?)
17283        .filter(|child| child.kind() == ";" && !child.is_missing())
17284}
17285
17286/// The known pre-ANSI prototype macros whose expansion is exactly their one
17287/// argument. The malformed CST cannot prove that an arbitrary identifier has
17288/// that definition, so unknown spellings fail closed rather than turning an
17289/// ordinary broken declaration into a Function (issue #2932).
17290fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
17291    matches!(
17292        node.kind(),
17293        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
17294    ) && matches!(
17295        normalize_cpp_whitespace(node_text(node, source)).as_str(),
17296        "_" | "__P" | "OF" | "PROTO"
17297    )
17298}
17299
17300/// Read tree-sitter's recovery for `declared_name MACRO`: a
17301/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
17302/// declared name, and whose `name` is a known prototype macro.
17303fn cpp_prototype_macro_qualified_parts<'tree>(
17304    node: Node<'tree>,
17305    source: &str,
17306) -> Option<(Node<'tree>, Node<'tree>)> {
17307    if node.kind() != "qualified_identifier" {
17308        return None;
17309    }
17310    let declared_name = node
17311        .child_by_field_name("scope")
17312        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
17313    let macro_name = macro_decorated_unqualified_name(node)?;
17314    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
17315}
17316
17317/// The inner parenthesized node of tree-sitter's structured
17318/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
17319/// the macro invocation's outer parentheses while retaining the one pair an
17320/// ordinary function declarator needs. No delimiter search is involved: both
17321/// pairs and their ownership come from the CST.
17322fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
17323    if arguments.kind() != "argument_list"
17324        || arguments.named_child_count() != 1
17325        || arguments.child_count() != 3
17326        || arguments
17327            .child(0)
17328            .is_none_or(|open| open.kind() != "(" || open.is_missing())
17329        || arguments
17330            .child(2)
17331            .is_none_or(|close| close.kind() != ")" || close.is_missing())
17332    {
17333        return None;
17334    }
17335    let inner = arguments.named_child(0)?;
17336    let close_index = match inner.kind() {
17337        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
17338        // tree-sitter completes the C++ cast production with a zero-width
17339        // value after the live close parenthesis.
17340        "cast_expression" => inner.child_count().checked_sub(2)?,
17341        _ => return None,
17342    };
17343    (inner
17344        .child(0)
17345        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
17346        && inner
17347            .child(close_index)
17348            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
17349    .then_some(inner)
17350}
17351
17352fn cpp_prototype_macro_candidate_from_init_declaration(
17353    declaration: Node<'_>,
17354    source: &str,
17355) -> Option<PrototypeMacroCandidate> {
17356    let init = declaration
17357        .child_by_field_name("declarator")
17358        .filter(|declarator| declarator.kind() == "init_declarator")?;
17359    let malformed_declarator = init.child_by_field_name("declarator")?;
17360    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
17361        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
17362    } else {
17363        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
17364            return None;
17365        }
17366        let declared_name_error = init
17367            .prev_named_sibling()
17368            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
17369        let declared_name = declared_name_error
17370            .named_child(0)
17371            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
17372        (declared_name, malformed_declarator)
17373    };
17374    let arguments = init
17375        .child_by_field_name("value")
17376        .filter(|value| value.kind() == "argument_list")?;
17377    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17378    let semicolon = cpp_direct_semicolon(declaration)?;
17379    let return_type = declaration.child_by_field_name("type")?;
17380    if return_type.end_byte() > declared_name.start_byte()
17381        || declared_name.end_byte() > macro_name.start_byte()
17382        || macro_name.end_byte() > arguments.start_byte()
17383        || arguments.end_byte() > semicolon.start_byte()
17384    {
17385        return None;
17386    }
17387    Some(PrototypeMacroCandidate {
17388        run_start: declaration.start_byte(),
17389        identifier_start: macro_name.start_byte(),
17390        inner_open_start: inner.start_byte(),
17391        inner_close_end: inner.end_byte(),
17392        outer_close_end: arguments.end_byte(),
17393        semicolon_end: semicolon.end_byte(),
17394    })
17395}
17396
17397fn cpp_prototype_macro_candidate_from_qualified_declaration(
17398    declaration: Node<'_>,
17399    source: &str,
17400) -> Option<PrototypeMacroCandidate> {
17401    let qualified = declaration
17402        .child_by_field_name("declarator")
17403        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
17404    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17405    let open_error = qualified
17406        .next_named_sibling()
17407        .filter(|next| next.kind() == "ERROR")?;
17408    let close_error = last_named_child(declaration)
17409        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
17410    if open_error.child_count() < 3
17411        || open_error
17412            .child(0)
17413            .is_none_or(|open| open.kind() != "(" || open.is_missing())
17414        || open_error
17415            .child(1)
17416            .is_none_or(|open| open.kind() != "(" || open.is_missing())
17417        || close_error.child_count() != 2
17418        || close_error
17419            .child(0)
17420            .is_none_or(|close| close.kind() != ")" || close.is_missing())
17421        || close_error
17422            .child(1)
17423            .is_none_or(|close| close.kind() != ")" || close.is_missing())
17424    {
17425        return None;
17426    }
17427    let inner_open = open_error.child(1)?;
17428    let inner_close = close_error.child(0)?;
17429    let outer_close = close_error.child(1)?;
17430    let semicolon = cpp_direct_semicolon(declaration)?;
17431    let return_type = declaration.child_by_field_name("type")?;
17432    if return_type.end_byte() > qualified.start_byte()
17433        || macro_name.end_byte() > open_error.start_byte()
17434        || inner_open.start_byte() > inner_close.end_byte()
17435        || inner_close.end_byte() > outer_close.start_byte()
17436        || outer_close.end_byte() > semicolon.start_byte()
17437    {
17438        return None;
17439    }
17440    Some(PrototypeMacroCandidate {
17441        run_start: declaration.start_byte(),
17442        identifier_start: macro_name.start_byte(),
17443        inner_open_start: inner_open.start_byte(),
17444        inner_close_end: inner_close.end_byte(),
17445        outer_close_end: outer_close.end_byte(),
17446        semicolon_end: semicolon.end_byte(),
17447    })
17448}
17449
17450fn cpp_prototype_macro_candidate_from_pointer_expression(
17451    statement: Node<'_>,
17452    source: &str,
17453) -> Option<PrototypeMacroCandidate> {
17454    if statement.kind() != "expression_statement"
17455        || statement.named_child_count() != 1
17456        || !statement.has_error()
17457    {
17458        return None;
17459    }
17460    let expansion = statement
17461        .named_child(0)
17462        .filter(|child| child.kind() == "parameter_pack_expansion")?;
17463    let binary = expansion
17464        .child_by_field_name("pattern")
17465        .filter(|pattern| pattern.kind() == "binary_expression")?;
17466    if binary.child_count() != 3
17467        || binary
17468            .child(1)
17469            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
17470        || expansion
17471            .child(expansion.child_count().checked_sub(1)?)
17472            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
17473    {
17474        return None;
17475    }
17476    let return_type = binary.child_by_field_name("left")?;
17477    let call = binary
17478        .child_by_field_name("right")
17479        .filter(|right| right.kind() == "call_expression")?;
17480    let qualified = call.child_by_field_name("function")?;
17481    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17482    let arguments = call
17483        .child_by_field_name("arguments")
17484        .filter(|arguments| arguments.kind() == "argument_list")?;
17485    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17486    let semicolon = cpp_direct_semicolon(statement)?;
17487    if return_type.end_byte() > declared_name.start_byte()
17488        || macro_name.end_byte() > arguments.start_byte()
17489        || arguments.end_byte() > semicolon.start_byte()
17490    {
17491        return None;
17492    }
17493    Some(PrototypeMacroCandidate {
17494        run_start: statement.start_byte(),
17495        identifier_start: macro_name.start_byte(),
17496        inner_open_start: inner.start_byte(),
17497        inner_close_end: inner.end_byte(),
17498        outer_close_end: arguments.end_byte(),
17499        semicolon_end: semicolon.end_byte(),
17500    })
17501}
17502
17503/// Recover only CST shapes whose fields preserve the declaration name, macro
17504/// invocation, nested argument list, and terminator. Arbitrary flattened
17505/// `ERROR` wreckage stays unsupported rather than being interpreted through a
17506/// token or delimiter scan (issue #2932).
17507fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
17508    let candidate = match node.kind() {
17509        "declaration" if node.has_error() => {
17510            cpp_prototype_macro_candidate_from_init_declaration(node, source)
17511                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
17512        }
17513        "expression_statement" => {
17514            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
17515        }
17516        _ => None,
17517    };
17518    candidate.into_iter().collect()
17519}
17520
17521fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
17522    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
17523        return false;
17524    }
17525    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
17526        return false;
17527    }
17528    let Some(declarator) = (if node.kind() == "function_definition" {
17529        node.child_by_field_name("declarator")
17530            .and_then(extract_function_declarator)
17531    } else {
17532        node.named_child(0)
17533            .filter(|child| child.kind() == "function_declarator")
17534    }) else {
17535        return false;
17536    };
17537    let Some(name) = cpp_function_declarator_name_node(declarator) else {
17538        return false;
17539    };
17540    declarator.start_byte() == node.start_byte()
17541        && name.kind() == "identifier"
17542        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
17543}
17544
17545/// Reparse a fragmented class-body interior while preserving its original byte
17546/// and line offsets, confined to the region by tree-sitter included ranges.
17547///
17548/// This used to materialize the region's whole file prefix as whitespace and
17549/// make the lexer walk it, the technique #1309 replaced on the other reparse
17550/// path: O(file) per fragmented-class recovery, on files that are already
17551/// error-recovered and already slow (#2788). Included ranges give the parser
17552/// the same view -- the region's bytes, at their original offsets and
17553/// line/column positions -- without materializing or lexing anything before it.
17554///
17555/// The equality of the two views is the claim, so a debug build parses both and
17556/// asserts the trees agree node for node.
17557fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
17558    let region = cpp_reparse_region_items(source, start, end);
17559
17560    #[cfg(debug_assertions)]
17561    assert_eq!(
17562        region.as_ref().map(cpp_tree_shape),
17563        cpp_reparse_padded_class_body(source, start, end)
17564            .as_ref()
17565            .map(cpp_tree_shape),
17566        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
17567         prefix produces"
17568    );
17569
17570    region
17571}
17572
17573/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
17574/// replaces, kept as the oracle a debug build asserts every region reparse
17575/// against and as the release-mode parity tests' reference (#2788).
17576#[cfg(any(debug_assertions, test))]
17577fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
17578    if start >= end {
17579        // The included-range parser refuses an empty region; the padded one
17580        // returned a tree holding nothing, which every caller read as "no
17581        // members here".
17582        return None;
17583    }
17584    let bytes = source.as_bytes();
17585    let prefix = bytes.get(..start)?;
17586    let interior = bytes.get(start..end)?;
17587    let mut padded = Vec::with_capacity(end);
17588    padded.extend(
17589        prefix
17590            .iter()
17591            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
17592    );
17593    padded.extend_from_slice(interior);
17594    let padded = String::from_utf8(padded).ok()?;
17595    let mut parser = Parser::new();
17596    parser
17597        .set_language(&tree_sitter_cpp::LANGUAGE.into())
17598        .ok()?;
17599    parser.parse(&padded, None)
17600}
17601
17602/// Every node of `tree` in preorder, by kind, span and position, which is what
17603/// two reparses of one region have to agree on for their callers to read the
17604/// same declarations out of either (#2788).
17605#[cfg(any(debug_assertions, test))]
17606fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
17607    let mut shape = Vec::new();
17608    let mut cursor = tree.root_node().walk();
17609    let mut stack = vec![tree.root_node()];
17610    while let Some(node) = stack.pop() {
17611        shape.push((
17612            node.kind(),
17613            node.start_byte(),
17614            node.end_byte(),
17615            node.start_position().row,
17616            node.start_position().column,
17617            node.is_named(),
17618            node.is_missing(),
17619        ));
17620        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
17621        stack.extend(children.into_iter().rev());
17622    }
17623    shape
17624}
17625
17626/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
17627/// reparsed interior is indexed only when every top-level named node is a
17628/// well-formed C++ item (or a comment) and at least one real item is present.
17629/// Expression/statement soup surfaces as a top-level `ERROR` or
17630/// `expression_statement`, neither of which is an item kind, so it is rejected.
17631///
17632/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
17633/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
17634/// swallowed by a preceding dangling sentinel) reparses to a real
17635/// `namespace_definition` whose body still holds a bogus `function_definition`,
17636/// so the subtree legitimately carries an error. Container items are admitted
17637/// even with an internal error; the inner bogus function is recovered recursively
17638/// when `visit_function_definition` walks it. Each recursion strips at least one
17639/// leading sentinel, so the region strictly shrinks and recovery terminates.
17640///
17641/// A top-level `function_definition` is the one place we stay strict: it is
17642/// admitted only when it is clean or is itself a sentinel candidate. A function
17643/// that has an error and is not a sentinel is a real callable with a broken body,
17644/// so we refuse the whole reparse and let the ordinary path handle it (preserving
17645/// its real return type rather than re-deriving an implicit one).
17646fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
17647    let mut cursor = root.walk();
17648    let mut saw_item = false;
17649    for child in root.named_children(&mut cursor) {
17650        match child.kind() {
17651            "comment" => {}
17652            "function_definition" => {
17653                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
17654                    return false;
17655                }
17656                saw_item = true;
17657            }
17658            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
17659            _ => return false,
17660        }
17661    }
17662    saw_item
17663}
17664
17665/// Robustness gate for a reparsed fragmented multiple-base export class body
17666/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
17667/// kinds a class body produces when reparsed at translation-unit scope: the
17668/// access-specifier label preceding the first member surfaces as a
17669/// `labeled_statement` wrapping that member, and members surface as
17670/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
17671/// Statement or expression soup surfaces as other top-level kinds and is rejected,
17672/// so only a genuinely member-shaped body is ever re-owned as members; anything
17673/// ambiguous falls back to indexing the class alone.
17674fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
17675    if node.kind() != "ERROR" {
17676        return false;
17677    }
17678    let mut stack = Vec::new();
17679    let mut saw_function_declarator = false;
17680    let mut cursor = node.walk();
17681    for child in node.named_children(&mut cursor) {
17682        stack.push(child);
17683    }
17684    while let Some(current) = stack.pop() {
17685        match current.kind() {
17686            // Tree-sitter may wrap adjacent copy-control declarations in a
17687            // nested ERROR. Keep descending only through ERROR wrappers; the
17688            // actual declaration payload must be a function_declarator.
17689            "ERROR" => {
17690                let mut cursor = current.walk();
17691                stack.extend(current.named_children(&mut cursor));
17692            }
17693            "function_declarator" => saw_function_declarator = true,
17694            _ => return false,
17695        }
17696    }
17697    saw_function_declarator
17698}
17699
17700fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
17701    if node.kind() != "ERROR" {
17702        return false;
17703    }
17704    let mut cursor = node.walk();
17705    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17706    let [explicit, constructor_error, destructor] = named.as_slice() else {
17707        return false;
17708    };
17709    let Some(constructor) = constructor_error.named_child(0) else {
17710        return false;
17711    };
17712    let Some(constructor_name) =
17713        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
17714    else {
17715        return false;
17716    };
17717    let Some(destructor_name) =
17718        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
17719    else {
17720        return false;
17721    };
17722    let Some(destroyed_type) = destructor_name.named_child(0) else {
17723        return false;
17724    };
17725    explicit.kind() == "explicit_function_specifier"
17726        && constructor_error.kind() == "ERROR"
17727        && constructor_error.named_child_count() == 1
17728        && constructor.kind() == "function_declarator"
17729        && constructor_name.kind() == "identifier"
17730        && destructor.kind() == "function_declarator"
17731        && destructor_name.kind() == "destructor_name"
17732        && destroyed_type.kind() == "identifier"
17733        && node_text(constructor_name, source) == node_text(destroyed_type, source)
17734}
17735
17736fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
17737    if node.kind() != "compound_statement" {
17738        return false;
17739    }
17740    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
17741        return false;
17742    };
17743    if prefix.kind() == "labeled_statement"
17744        && prefix.named_child(0).is_some_and(|label| {
17745            matches!(
17746                node_text(label, source).trim(),
17747                "public" | "private" | "protected"
17748            )
17749        })
17750    {
17751        return prefix.named_children(&mut prefix.walk()).any(|child| {
17752            child.kind() == "declaration"
17753                && child.has_error()
17754                && child
17755                    .named_children(&mut child.walk())
17756                    .any(cpp_reparsed_member_error_is_indexable)
17757        });
17758    }
17759    // A malformed constructor initializer can be split into a declaration
17760    // followed by its compound body when the class prefix already contains
17761    // realistic members. Keep this admission tied to that exact structured
17762    // declaration/error/body chain rather than accepting arbitrary blocks.
17763    prefix.kind() == "declaration"
17764        && prefix.has_error()
17765        && prefix
17766            .named_children(&mut prefix.walk())
17767            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
17768}
17769
17770fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
17771    if !cpp_reparsed_member_error_is_indexable(node) {
17772        return false;
17773    }
17774    let Some(preproc) = node.next_named_sibling() else {
17775        return false;
17776    };
17777    preproc.kind() == "preproc_if"
17778        && preproc.has_error()
17779        && preproc
17780            .named_children(&mut preproc.walk())
17781            .any(|child| child.kind() == "expression_statement" && child.has_error())
17782        && preproc
17783            .next_named_sibling()
17784            .is_some_and(|body| body.kind() == "compound_statement")
17785}
17786
17787/// Return a function body whose braces and ownership are explicit in the
17788/// reparsed class-member tree. An error below a real function envelope is
17789/// recoverable by the ordinary function visitor; a missing/deferred body is
17790/// not, because accepting it would let statement soup masquerade as a member.
17791fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
17792    if node.kind() != "function_definition" {
17793        return None;
17794    }
17795    let body = node.child_by_field_name("body")?;
17796    if body.kind() != "compound_statement" {
17797        return None;
17798    }
17799    let open = body.child(0)?;
17800    let close = body.child(body.child_count().checked_sub(1)?)?;
17801    if open.kind() != "{"
17802        || open.is_missing()
17803        || close.kind() != "}"
17804        || close.is_missing()
17805        || close.end_byte() != body.end_byte()
17806        || body.end_byte() != node.end_byte()
17807    {
17808        return None;
17809    }
17810    Some(body)
17811}
17812
17813fn cpp_reparsed_member_function_errors_are_in_body(
17814    node: Node<'_>,
17815    body: Node<'_>,
17816    source: &str,
17817) -> bool {
17818    let mut cursor = node.walk();
17819    node.children(&mut cursor).all(|child| {
17820        same_node(child, body)
17821            || cpp_reparsed_member_attribute_error(child, source)
17822            || cpp_reparsed_member_signature_identifier_errors(child)
17823            || (!child.has_error() && !child.is_error() && !child.is_missing())
17824    })
17825}
17826
17827/// A complete callable can still carry parser errors in its signature when a
17828/// project annotation is not part of the C++ grammar (`nonneg int`,
17829/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
17830/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
17831/// leaves inside the already-proven callable envelope; structured statements,
17832/// literals, missing tokens, and other malformed signature payload remain
17833/// rejected.
17834fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
17835    if !node.has_error() && !node.is_error() && !node.is_missing() {
17836        return false;
17837    }
17838    let mut stack = vec![node];
17839    let mut saw_error = false;
17840    while let Some(current) = stack.pop() {
17841        if current.is_missing() {
17842            return false;
17843        }
17844        if current.kind() == "ERROR" {
17845            saw_error = true;
17846            let mut cursor = current.walk();
17847            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17848            if children
17849                .iter()
17850                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
17851            {
17852                return false;
17853            }
17854            stack.extend(children);
17855            continue;
17856        }
17857        let mut cursor = current.walk();
17858        stack.extend(current.children(&mut cursor));
17859    }
17860    saw_error
17861}
17862
17863fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
17864    node.kind() == "ERROR"
17865        && node.named_child_count() == 1
17866        && node.named_child(0).is_some_and(|attribute| {
17867            attribute.kind() == "identifier"
17868                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
17869        })
17870}
17871
17872/// A C++ attribute placed between a member's declarator and body can make
17873/// tree-sitter expose the callable as
17874/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
17875/// Keep this admission tied to that exact node geometry. In particular, an
17876/// arbitrary ERROR or identifier before a compound statement is not enough.
17877fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
17878    let Some(body) = cpp_reparsed_member_function_body(node) else {
17879        return false;
17880    };
17881    let mut cursor = node.walk();
17882    let named = node
17883        .named_children(&mut cursor)
17884        .filter(|child| child.kind() != "comment")
17885        .collect::<Vec<_>>();
17886    let [type_node, error, attribute, body_node] = named.as_slice() else {
17887        return false;
17888    };
17889    if !same_node(*body_node, body)
17890        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
17891        || attribute.kind() != "identifier"
17892        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
17893        || error.kind() != "ERROR"
17894        || error.named_child_count() != 1
17895    {
17896        return false;
17897    }
17898    error
17899        .named_child(0)
17900        .is_some_and(cpp_reparsed_attribute_callable_declarator)
17901}
17902
17903fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
17904    cpp_structured_type_path(node, source).is_some()
17905        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
17906}
17907
17908fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17909    let Some(body) = cpp_reparsed_member_function_body(node) else {
17910        return false;
17911    };
17912    let mut cursor = node.walk();
17913    let named = node
17914        .named_children(&mut cursor)
17915        .filter(|child| child.kind() != "comment")
17916        .collect::<Vec<_>>();
17917    let [friend, return_error, declarator, body_node] = named.as_slice() else {
17918        return false;
17919    };
17920    let Some(return_type) = return_error.named_child(0) else {
17921        return false;
17922    };
17923    same_node(*body_node, body)
17924        && friend.kind() == "type_identifier"
17925        && node_text(*friend, source) == "friend"
17926        && return_error.kind() == "ERROR"
17927        && return_error.named_child_count() == 1
17928        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
17929        && extract_function_declarator(*declarator)
17930            .and_then(cpp_function_declarator_name_node)
17931            .is_some()
17932}
17933
17934fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17935    let Some(body) = cpp_reparsed_member_function_body(node) else {
17936        return false;
17937    };
17938    let mut cursor = node.walk();
17939    let named = node
17940        .named_children(&mut cursor)
17941        .filter(|child| child.kind() != "comment")
17942        .collect::<Vec<_>>();
17943    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
17944        return false;
17945    };
17946    let Some(return_type) = return_error.named_child(0) else {
17947        return false;
17948    };
17949    same_node(*body_node, body)
17950        && prefix
17951            .iter()
17952            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
17953        && attribute.kind() == "type_identifier"
17954        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
17955        && return_error.kind() == "ERROR"
17956        && return_error.named_child_count() == 1
17957        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
17958        && extract_function_declarator(*declarator)
17959            .and_then(cpp_function_declarator_name_node)
17960            .is_some()
17961}
17962
17963/// An included-range reparse that begins inside a malformed class can merge an
17964/// access label and following template member. Tree-sitter then emits the label
17965/// as the `template_type` name, the template parameter list as its arguments,
17966/// an ERROR-wrapped return type, the callable declarator, and its complete
17967/// body. Admit only that exact structured displacement.
17968fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17969    let Some(body) = cpp_reparsed_member_function_body(node) else {
17970        return false;
17971    };
17972    let mut cursor = node.walk();
17973    let named = node
17974        .named_children(&mut cursor)
17975        .filter(|child| child.kind() != "comment")
17976        .collect::<Vec<_>>();
17977    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
17978        return false;
17979    };
17980    let Some(template_name) = template_type.child_by_field_name("name") else {
17981        return false;
17982    };
17983    let Some(arguments) = template_type.child_by_field_name("arguments") else {
17984        return false;
17985    };
17986    let Some(return_type) = return_error.named_child(0) else {
17987        return false;
17988    };
17989    let mut cursor = template_type.walk();
17990    let template_errors = template_type
17991        .named_children(&mut cursor)
17992        .filter(|child| child.kind() == "ERROR")
17993        .collect::<Vec<_>>();
17994    let [comment_error] = template_errors.as_slice() else {
17995        return false;
17996    };
17997    let mut cursor = comment_error.walk();
17998    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
17999    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
18000        return false;
18001    };
18002    same_node(*body_node, body)
18003        && template_type.kind() == "template_type"
18004        && template_name.kind() == "type_identifier"
18005        && matches!(
18006            node_text(template_name, source).trim(),
18007            "public" | "private" | "protected"
18008        )
18009        && arguments.kind() == "template_argument_list"
18010        && arguments.named_child_count() > 0
18011        && !arguments.has_error()
18012        && !colon.is_named()
18013        && colon.kind() == ":"
18014        && comments.iter().all(|child| child.kind() == "comment")
18015        && !template_keyword.is_named()
18016        && template_keyword.kind() == "template"
18017        && return_error.kind() == "ERROR"
18018        && return_error.named_child_count() == 1
18019        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
18020        && extract_function_declarator(*declarator)
18021            .and_then(cpp_function_declarator_name_node)
18022            .is_some()
18023}
18024
18025/// Return the constructor declaration tree-sitter can merge into an access
18026/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
18027/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
18028/// declaration's apparent type; the callable name must still exactly match the
18029/// recovered class, so unrelated labeled statements are never re-owned.
18030fn cpp_reparsed_preprocessor_constructor<'tree>(
18031    node: Node<'tree>,
18032    class_name: &str,
18033    source: &str,
18034) -> Option<Node<'tree>> {
18035    if node.kind() != "labeled_statement" {
18036        return None;
18037    }
18038    let mut cursor = node.walk();
18039    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18040    let [label, directive_error, declaration] = named.as_slice() else {
18041        return None;
18042    };
18043    if label.kind() != "statement_identifier"
18044        || !matches!(
18045            node_text(*label, source),
18046            "public" | "private" | "protected"
18047        )
18048        || directive_error.kind() != "ERROR"
18049        || directive_error.child_count() != 1
18050        || directive_error
18051            .child(0)
18052            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
18053        || declaration.kind() != "declaration"
18054        || declaration.named_child_count() != 2
18055    {
18056        return None;
18057    }
18058    let apparent_type = declaration.child_by_field_name("type")?;
18059    if apparent_type.kind() != "type_identifier"
18060        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
18061    {
18062        return None;
18063    }
18064    let declarator = declaration.child_by_field_name("declarator")?;
18065    let function = extract_function_declarator(declarator)?;
18066    let name = cpp_function_declarator_name_node(function)?;
18067    (node_text(name, source) == class_name).then_some(*declaration)
18068}
18069
18070fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
18071    if extract_function_declarator(node)
18072        .and_then(cpp_function_declarator_name_node)
18073        .is_some()
18074    {
18075        return true;
18076    }
18077    node.kind() == "init_declarator"
18078        && node
18079            .child_by_field_name("declarator")
18080            .is_some_and(|declarator| declarator.kind() == "identifier")
18081        && node
18082            .child_by_field_name("value")
18083            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
18084}
18085
18086/// Return true for the constrained/attribute form that tree-sitter splits into
18087/// an ERROR declaration, a preprocessor `requires` clause, and a following
18088/// compound statement. The three nodes must remain immediate named siblings;
18089/// this deliberately does not search source text or skip unrelated statements.
18090fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
18091    if node.kind() != "ERROR" || node.named_child_count() != 3 {
18092        return false;
18093    }
18094    let mut cursor = node.walk();
18095    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18096    let [type_node, function_declarator, attribute] = named.as_slice() else {
18097        return false;
18098    };
18099    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18100        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
18101        || attribute.kind() != "identifier"
18102        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18103    {
18104        return false;
18105    }
18106    let Some(preproc) =
18107        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18108    else {
18109        return false;
18110    };
18111    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
18112        .filter(|sibling| sibling.kind() == "compound_statement")
18113    else {
18114        return false;
18115    };
18116    let Some(open) = body.child(0) else {
18117        return false;
18118    };
18119    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
18120        return false;
18121    };
18122    let Some(condition) = preproc.child_by_field_name("condition") else {
18123        return false;
18124    };
18125    let mut cursor = preproc.walk();
18126    let payload = preproc
18127        .named_children(&mut cursor)
18128        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
18129        .collect::<Vec<_>>();
18130    let [requires_statement] = payload.as_slice() else {
18131        return false;
18132    };
18133    let requires_clause = requires_statement.named_child(0);
18134
18135    open.kind() == "{"
18136        && !open.is_missing()
18137        && close.kind() == "}"
18138        && !close.is_missing()
18139        && close.end_byte() == body.end_byte()
18140        && requires_statement.kind() == "expression_statement"
18141        && requires_statement.named_child_count() == 1
18142        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
18143}
18144
18145fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18146    let mut sibling = node.next_named_sibling();
18147    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18148        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
18149    }
18150    sibling
18151}
18152
18153fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18154    let mut sibling = node.prev_named_sibling();
18155    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18156        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
18157    }
18158    sibling
18159}
18160
18161fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
18162    let Some(preproc) =
18163        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18164    else {
18165        return false;
18166    };
18167    let Some(error) =
18168        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
18169    else {
18170        return false;
18171    };
18172    cpp_reparsed_attribute_requires_error(error, source)
18173}
18174
18175fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
18176    node: Node<'tree>,
18177    source: &str,
18178) -> Option<Node<'tree>> {
18179    if node.kind() != "ERROR" {
18180        return None;
18181    }
18182    let mut cursor = node.walk();
18183    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18184    let [parameter, macro_name, message] = named.as_slice() else {
18185        return None;
18186    };
18187    let parameter_name = parameter.named_child(0)?;
18188    (parameter.kind() == "type_parameter_declaration"
18189        && parameter_name.kind() == "type_identifier"
18190        && macro_name.kind() == "type_identifier"
18191        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
18192        && message.kind() == "string_literal")
18193        .then_some(parameter_name)
18194}
18195
18196/// Recognize the alternate constraint-macro prefix where tree-sitter retains
18197/// the complete qualified constraint as a fourth child instead of moving it
18198/// into the following function. Keep the gate tied to a two-type template
18199/// constraint that names the declared type parameter.
18200fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
18201    node: Node<'tree>,
18202    source: &str,
18203) -> Option<Node<'tree>> {
18204    if node.kind() != "ERROR" {
18205        return None;
18206    }
18207    let mut cursor = node.walk();
18208    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18209    let [parameter, macro_name, message, constraint] = named.as_slice() else {
18210        return None;
18211    };
18212    let parameter_name = parameter.named_child(0)?;
18213    let constraint_scope = constraint.child_by_field_name("scope")?;
18214    let constraint_template = constraint.child_by_field_name("name")?;
18215    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18216    let mut argument_cursor = constraint_arguments.walk();
18217    let constraint_types = constraint_arguments
18218        .named_children(&mut argument_cursor)
18219        .collect::<Vec<_>>();
18220    if parameter.kind() != "type_parameter_declaration"
18221        || parameter_name.kind() != "type_identifier"
18222        || macro_name.kind() != "type_identifier"
18223        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
18224        || message.kind() != "string_literal"
18225        || constraint.kind() != "qualified_identifier"
18226        || constraint_scope.kind() != "namespace_identifier"
18227        || !matches!(
18228            constraint_template.kind(),
18229            "template_function" | "template_type"
18230        )
18231        || !matches!(constraint_types.as_slice(), [left, right]
18232            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18233        || constraint_arguments.has_error()
18234    {
18235        return None;
18236    }
18237    let parameter_text = node_text(parameter_name, source);
18238    let mut stack = constraint_types;
18239    while let Some(current) = stack.pop() {
18240        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
18241            return Some(parameter_name);
18242        }
18243        let mut cursor = current.walk();
18244        stack.extend(current.named_children(&mut cursor));
18245    }
18246    None
18247}
18248
18249fn cpp_reparsed_template_macro_companion_is_indexable(
18250    node: Node<'_>,
18251    parameter_name: Node<'_>,
18252    source: &str,
18253) -> bool {
18254    let Some(body) = cpp_reparsed_member_function_body(node) else {
18255        return false;
18256    };
18257    let mut cursor = node.walk();
18258    let named = node
18259        .named_children(&mut cursor)
18260        .filter(|child| child.kind() != "comment")
18261        .collect::<Vec<_>>();
18262    let [
18263        constraint,
18264        close_error,
18265        storage,
18266        return_error,
18267        declarator,
18268        body_node,
18269    ] = named.as_slice()
18270    else {
18271        return false;
18272    };
18273    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
18274        return false;
18275    };
18276    let Some(constraint_template) = constraint.child_by_field_name("name") else {
18277        return false;
18278    };
18279    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
18280        return false;
18281    };
18282    let Some(return_type) = return_error.named_child(0) else {
18283        return false;
18284    };
18285    let mut cursor = constraint_arguments.walk();
18286    let constraint_types = constraint_arguments
18287        .named_children(&mut cursor)
18288        .collect::<Vec<_>>();
18289    same_node(*body_node, body)
18290        && constraint.kind() == "qualified_identifier"
18291        && constraint_scope.kind() == "namespace_identifier"
18292        && constraint_template.kind() == "template_type"
18293        && matches!(constraint_types.as_slice(), [left, right]
18294            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18295        && !constraint_arguments.has_error()
18296        && close_error.kind() == "ERROR"
18297        && close_error.named_child_count() == 0
18298        && storage.kind() == "storage_class_specifier"
18299        && return_error.kind() == "ERROR"
18300        && return_error.named_child_count() == 1
18301        && return_type.kind() == "identifier"
18302        && node_text(return_type, source) == node_text(parameter_name, source)
18303        && extract_function_declarator(*declarator)
18304            .and_then(cpp_function_declarator_name_node)
18305            .is_some()
18306}
18307
18308fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
18309    node: Node<'tree>,
18310    parameter_name: Node<'_>,
18311    source: &str,
18312) -> Option<Node<'tree>> {
18313    let body = cpp_reparsed_member_function_body(node)?;
18314    let constraint = node.child_by_field_name("type")?;
18315    let constraint_template = constraint.child_by_field_name("name")?;
18316    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18317    let mut argument_cursor = constraint_arguments.walk();
18318    let constraint_types = constraint_arguments
18319        .named_children(&mut argument_cursor)
18320        .collect::<Vec<_>>();
18321    if constraint.kind() != "qualified_identifier"
18322        || constraint_template.kind() != "template_type"
18323        || !matches!(constraint_types.as_slice(), [left, right]
18324            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18325        || constraint_arguments.has_error()
18326        || node
18327            .child_by_field_name("body")
18328            .is_none_or(|candidate| !same_node(candidate, body))
18329    {
18330        return None;
18331    }
18332
18333    let mut cursor = node.walk();
18334    let recovery_errors = node
18335        .named_children(&mut cursor)
18336        .filter(|child| child.kind() == "ERROR")
18337        .collect::<Vec<_>>();
18338    if !recovery_errors
18339        .iter()
18340        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
18341        || !recovery_errors.iter().all(|error| {
18342            error.named_child_count() == 0
18343                || cpp_reparsed_constraint_macro_error(*error, source)
18344                || (error.named_child_count() == 1
18345                    && error
18346                        .named_child(0)
18347                        .is_some_and(|child| child.kind() == "function_declarator"))
18348        })
18349    {
18350        return None;
18351    }
18352
18353    let parameter_text = node_text(parameter_name, source);
18354    let mut declarators = node
18355        .child_by_field_name("declarator")
18356        .and_then(extract_function_declarator)
18357        .into_iter()
18358        .collect::<Vec<_>>();
18359    for error in recovery_errors {
18360        let mut stack = vec![error];
18361        while let Some(current) = stack.pop() {
18362            if current.kind() == "function_declarator" {
18363                declarators.push(current);
18364            }
18365            let mut cursor = current.walk();
18366            stack.extend(current.named_children(&mut cursor));
18367        }
18368    }
18369    declarators.into_iter().find(|declarator| {
18370        cpp_function_declarator_name_node(*declarator)
18371            .is_some_and(|name| name.kind() == "identifier")
18372            && declarator
18373                .child_by_field_name("parameters")
18374                .is_some_and(|parameters| {
18375                    parameters
18376                        .named_children(&mut parameters.walk())
18377                        .filter_map(|parameter| parameter.child_by_field_name("type"))
18378                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
18379                })
18380    })
18381}
18382
18383fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
18384    node: Node<'_>,
18385    parameter_name: Node<'_>,
18386    source: &str,
18387) -> bool {
18388    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
18389}
18390
18391fn cpp_reparsed_template_macro_function_companion_is_indexable(
18392    node: Node<'_>,
18393    parameter_name: Node<'_>,
18394    source: &str,
18395) -> bool {
18396    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
18397        return false;
18398    }
18399    let Some(return_type) = node.child_by_field_name("type") else {
18400        return false;
18401    };
18402    let Some(function_declarator) = node
18403        .child_by_field_name("declarator")
18404        .and_then(extract_function_declarator)
18405    else {
18406        return false;
18407    };
18408    if cpp_function_declarator_name_node(function_declarator).is_none()
18409        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
18410    {
18411        return false;
18412    }
18413    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
18414        return false;
18415    };
18416    let parameter_text = node_text(parameter_name, source);
18417    parameters
18418        .named_children(&mut parameters.walk())
18419        .any(|parameter| {
18420            parameter
18421                .child_by_field_name("type")
18422                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
18423        })
18424}
18425
18426fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
18427    if node.kind() != "ERROR" {
18428        return false;
18429    }
18430    let mut stack = vec![node];
18431    while let Some(current) = stack.pop() {
18432        let macro_shape = match current.kind() {
18433            "call_expression" => current
18434                .child_by_field_name("function")
18435                .zip(current.child_by_field_name("arguments")),
18436            "init_declarator" => current
18437                .child_by_field_name("declarator")
18438                .zip(current.child_by_field_name("value")),
18439            _ => None,
18440        };
18441        if let Some((name, arguments)) = macro_shape
18442            && name.kind() == "identifier"
18443            && arguments.kind() == "argument_list"
18444            && arguments.named_child_count() >= 2
18445            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18446        {
18447            return true;
18448        }
18449        let mut cursor = current.walk();
18450        stack.extend(current.named_children(&mut cursor));
18451    }
18452    false
18453}
18454
18455fn cpp_recovered_template_macro_constructor<'tree>(
18456    node: Node<'tree>,
18457    source: &str,
18458) -> Option<(Node<'tree>, Node<'tree>)> {
18459    let mut prefix = node.prev_named_sibling()?;
18460    while prefix.kind() == "comment" {
18461        prefix = prefix.prev_named_sibling()?;
18462    }
18463    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
18464    let parameter = parameter_name
18465        .parent()
18466        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
18467    let declarator =
18468        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
18469    Some((declarator, parameter))
18470}
18471
18472fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
18473    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
18474        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18475            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
18476                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
18477                    function,
18478                    parameter_name,
18479                    source,
18480                )
18481        });
18482    }
18483    let Some(parameter_name) =
18484        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
18485    else {
18486        return false;
18487    };
18488    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18489        cpp_reparsed_template_macro_function_companion_is_indexable(
18490            function,
18491            parameter_name,
18492            source,
18493        )
18494    })
18495}
18496
18497fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18498    let function_name = node
18499        .child_by_field_name("declarator")
18500        .and_then(extract_function_declarator)
18501        .and_then(cpp_function_declarator_name_node);
18502    if let Some(body) = cpp_reparsed_member_function_body(node)
18503        && function_name.is_some()
18504        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
18505    {
18506        return true;
18507    }
18508    cpp_reparsed_attribute_member_function(node, source)
18509        || cpp_reparsed_friend_function_is_indexable(node, source)
18510        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
18511        || cpp_reparsed_access_template_function_is_indexable(node, source)
18512        || cpp_recovered_template_macro_constructor(node, source).is_some()
18513}
18514
18515/// Recognize the three top-level nodes produced when an unknown attribute
18516/// macro separates an inline member's declarator from its body in a reparsed
18517/// class interior: an errorful declaration with a missing semicolon, the macro
18518/// call expression, and the complete compound body. Their adjacency and exact
18519/// structured shapes prove one recoverable member envelope; arbitrary calls or
18520/// blocks do not pass this gate.
18521fn cpp_reparsed_macro_attribute_member_sequence(
18522    children: &[Node<'_>],
18523    index: usize,
18524    source: &str,
18525) -> bool {
18526    let Some(prefix) = children.get(index).copied() else {
18527        return false;
18528    };
18529    let declaration = if prefix.kind() == "labeled_statement" {
18530        prefix
18531            .named_child(prefix.named_child_count().saturating_sub(1))
18532            .filter(|child| child.kind() == "declaration")
18533    } else {
18534        (prefix.kind() == "declaration").then_some(prefix)
18535    };
18536    let Some(declaration) = declaration else {
18537        return false;
18538    };
18539    if !declaration.has_error()
18540        || declaration
18541            .child_by_field_name("declarator")
18542            .and_then(extract_function_declarator)
18543            .and_then(cpp_function_declarator_name_node)
18544            .is_none()
18545    {
18546        return false;
18547    }
18548    let Some(attribute_statement) = children.get(index + 1).copied() else {
18549        return false;
18550    };
18551    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
18552        .then(|| attribute_statement.named_child(0))
18553        .flatten()
18554        .filter(|child| child.kind() == "call_expression")
18555    else {
18556        return false;
18557    };
18558    let Some(attribute_name) = attribute_call
18559        .child_by_field_name("function")
18560        .filter(|function| function.kind() == "identifier")
18561        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
18562    else {
18563        return false;
18564    };
18565    if !cpp_export_macro_token(&attribute_name) {
18566        return false;
18567    }
18568    let Some(body) = children.get(index + 2).copied() else {
18569        return false;
18570    };
18571    body.kind() == "compound_statement"
18572        && body.child(0).is_some_and(|open| open.kind() == "{")
18573        && body
18574            .child(body.child_count().saturating_sub(1))
18575            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
18576        && declaration.end_byte() <= attribute_statement.start_byte()
18577        && attribute_statement.end_byte() <= body.start_byte()
18578}
18579
18580/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
18581/// types and specifiers followed by a declarator, over and over, with nothing
18582/// left over. A string-argument attribute macro is what strands them there, and
18583/// the walk indexes exactly what this reads, so admitting the region is safe
18584/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
18585/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
18586/// the whole class body.
18587fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
18588    if node.kind() != "ERROR" {
18589        return false;
18590    }
18591    let run = stranded_declaration_run(node, source);
18592    run.complete && !run.declarations.is_empty()
18593}
18594
18595fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
18596    let mut cursor = root.walk();
18597    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
18598    let mut saw_member = false;
18599    let mut index = 0;
18600    while index < children.len() {
18601        let child = children[index];
18602        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
18603            saw_member = true;
18604            index += 3;
18605            continue;
18606        }
18607        if let Some(recovered) = fragmented_class_body(child, source) {
18608            let Some(tree) = cpp_reparse_fragmented_class_body(
18609                source,
18610                recovered.body.reparse_start,
18611                recovered.body.reparse_end,
18612            ) else {
18613                return false;
18614            };
18615            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
18616                return false;
18617            }
18618            saw_member = true;
18619            index += 1;
18620            while index < children.len()
18621                && children[index].end_byte() <= recovered.body.class_range.end_byte
18622            {
18623                index += 1;
18624            }
18625            continue;
18626        }
18627        match child.kind() {
18628            "comment" => {}
18629            "labeled_statement" => saw_member = true,
18630            "function_definition" => {
18631                if child.has_error()
18632                    && !cpp_reparsed_member_function_is_indexable(child, source)
18633                    && cpp_sentinel_macro_region(child, source).is_none()
18634                {
18635                    return false;
18636                }
18637                saw_member = true;
18638            }
18639            // The attribute a string-argument macro leaves as a call statement
18640            // of its own. It declares nothing; the member it decorated is the
18641            // sibling after it, checked on its own turn.
18642            "expression_statement" if is_string_attribute_macro_statement(child) => {}
18643            "ERROR"
18644                if (cpp_reparsed_member_error_is_indexable(child)
18645                    || cpp_reparsed_adjacent_copy_control_error(child, source)
18646                    || cpp_reparsed_stranded_member_error(child, source))
18647                    && (child
18648                        .next_named_sibling()
18649                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
18650                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
18651            {
18652                saw_member = true;
18653            }
18654            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
18655                saw_member = true;
18656            }
18657            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
18658                saw_member = true;
18659            }
18660            "expression_statement"
18661                if cpp_is_stray_semicolon(child, source)
18662                    && child.prev_named_sibling().is_some_and(|error| {
18663                        cpp_reparsed_member_error_is_indexable(error)
18664                            || cpp_reparsed_adjacent_copy_control_error(error, source)
18665                            || cpp_reparsed_stranded_member_error(error, source)
18666                    }) =>
18667            {
18668                saw_member = true;
18669            }
18670            "compound_statement"
18671                if cpp_reparsed_constructor_body_is_indexable(child, source)
18672                    || cpp_reparsed_attribute_requires_body(child, source) =>
18673            {
18674                saw_member = true;
18675            }
18676            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
18677            _ => return false,
18678        }
18679        index += 1;
18680    }
18681    saw_member
18682}
18683
18684/// Detect the malformed constructor shape that tree-sitter exposes as an
18685/// access-label statement followed by initializer-looking declarations. The
18686/// declarations are not class members: visiting their `location(loc)` and
18687/// `string(s)` function declarators would publish synthetic functions. The
18688/// export-class fallback keeps the original sibling nodes and therefore avoids
18689/// this parser artifact. The returned range identifies the real constructor
18690/// header, which can be reparsed independently as a structured declarator.
18691fn cpp_reparsed_synthetic_initializer_constructor_range(
18692    root: Node<'_>,
18693    class_name: &str,
18694    source: &str,
18695    constructor_end: usize,
18696) -> Option<std::ops::Range<usize>> {
18697    let mut stack = {
18698        let mut cursor = root.walk();
18699        root.named_children(&mut cursor).collect::<Vec<_>>()
18700    };
18701    while let Some(current) = stack.pop() {
18702        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
18703            current,
18704            class_name,
18705            source,
18706            constructor_end,
18707        ) {
18708            return Some(range);
18709        }
18710        if current.kind() == "ERROR" {
18711            let mut cursor = current.walk();
18712            stack.extend(current.named_children(&mut cursor));
18713        }
18714    }
18715    None
18716}
18717
18718/// Recover an inline constructor that a function-like export macro makes
18719/// tree-sitter merge with the following overload. In the reparsed class-body
18720/// region, the access label wraps one declaration whose ERROR contains the
18721/// constructor declarator and its base-initializer/body, while the declaration's
18722/// ordinary declarator is the following overload. Every boundary below comes
18723/// from that CST; no source syntax is reparsed by hand.
18724fn cpp_reparsed_merged_inline_constructor<'tree>(
18725    root: Node<'tree>,
18726    class_name: &str,
18727    source: &str,
18728) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
18729    let mut stack = vec![root];
18730    while let Some(current) = stack.pop() {
18731        if current.kind() != "labeled_statement" {
18732            let mut cursor = current.walk();
18733            stack.extend(current.named_children(&mut cursor));
18734            continue;
18735        }
18736        let declaration = current
18737            .named_children(&mut current.walk())
18738            .find(|child| child.kind() == "declaration")?;
18739        if declaration
18740            .child_by_field_name("type")
18741            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
18742        {
18743            continue;
18744        }
18745        let following = declaration
18746            .child_by_field_name("declarator")
18747            .and_then(extract_function_declarator)
18748            .and_then(cpp_function_declarator_name_node);
18749        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
18750            continue;
18751        }
18752        let mut declaration_cursor = declaration.walk();
18753        let Some(error) = declaration
18754            .named_children(&mut declaration_cursor)
18755            .find(|child| child.kind() == "ERROR")
18756        else {
18757            continue;
18758        };
18759        let mut error_cursor = error.walk();
18760        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
18761        let Some(constructor) = error_children.iter().copied().find(|child| {
18762            child.kind() == "function_declarator"
18763                && cpp_function_declarator_name_node(*child)
18764                    .is_some_and(|name| node_text(name, source).trim() == class_name)
18765        }) else {
18766            continue;
18767        };
18768        let Some(body) = error_children.iter().copied().find_map(|child| {
18769            (child.kind() == "init_declarator")
18770                .then(|| child.child_by_field_name("value"))
18771                .flatten()
18772                .filter(|value| value.kind() == "initializer_list")
18773        }) else {
18774            continue;
18775        };
18776        if constructor.end_byte() > body.start_byte() {
18777            continue;
18778        }
18779        return Some((constructor.start_byte()..body.end_byte(), body));
18780    }
18781    None
18782}
18783
18784fn cpp_reparsed_synthetic_initializer_constructor(
18785    node: Node<'_>,
18786    class_name: &str,
18787    source: &str,
18788    constructor_end: usize,
18789) -> Option<std::ops::Range<usize>> {
18790    if node.kind() != "labeled_statement" {
18791        return None;
18792    }
18793    let mut cursor = node.walk();
18794    let named = node
18795        .named_children(&mut cursor)
18796        .filter(|child| child.kind() != "comment")
18797        .collect::<Vec<_>>();
18798    let label = named.first()?;
18799    if label.kind() != "statement_identifier"
18800        || !matches!(
18801            node_text(*label, source).trim(),
18802            "public" | "private" | "protected"
18803        )
18804    {
18805        return None;
18806    }
18807    let call_error_index = named.iter().position(|child| {
18808        if child.kind() != "ERROR" {
18809            return false;
18810        }
18811        let mut stack = vec![*child];
18812        while let Some(current) = stack.pop() {
18813            if current.kind() == "call_expression"
18814                && current
18815                    .child_by_field_name("function")
18816                    .is_some_and(|function| {
18817                        function.kind() == "identifier"
18818                            && node_text(function, source).trim() == class_name
18819                    })
18820            {
18821                return true;
18822            }
18823            let mut cursor = current.walk();
18824            stack.extend(current.named_children(&mut cursor));
18825        }
18826        false
18827    })?;
18828    let constructor_call = {
18829        let mut stack = vec![named[call_error_index]];
18830        let mut found = None;
18831        while let Some(current) = stack.pop() {
18832            if current.kind() == "call_expression"
18833                && current
18834                    .child_by_field_name("function")
18835                    .is_some_and(|function| {
18836                        function.kind() == "identifier"
18837                            && node_text(function, source).trim() == class_name
18838                    })
18839            {
18840                found = Some(current);
18841                break;
18842            }
18843            let mut cursor = current.walk();
18844            stack.extend(current.named_children(&mut cursor));
18845        }
18846        found
18847    };
18848    let constructor_call = constructor_call?;
18849    named.iter().skip(call_error_index + 1).find(|child| {
18850        child.kind() == "declaration" && child.has_error() && {
18851            let mut cursor = child.walk();
18852            child.named_children(&mut cursor).any(|declarator| {
18853                declarator.kind() == "init_declarator"
18854                    && declarator
18855                        .child_by_field_name("declarator")
18856                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
18857                    && declarator
18858                        .child_by_field_name("value")
18859                        .is_some_and(|value| value.kind() == "initializer_list")
18860            })
18861        }
18862    })?;
18863    Some(constructor_call.start_byte()..constructor_end)
18864}
18865
18866fn cpp_reparsed_exact_constructor_declarator<'tree>(
18867    root: Node<'tree>,
18868    start: usize,
18869    class_name: &str,
18870    source: &str,
18871) -> Option<Node<'tree>> {
18872    let mut candidate = None;
18873    let mut stack = vec![root];
18874    while let Some(current) = stack.pop() {
18875        if current.kind() == "function_declarator"
18876            && current.start_byte() == start
18877            && cpp_function_declarator_name_node(current)
18878                .is_some_and(|name| node_text(name, source).trim() == class_name)
18879        {
18880            if candidate.is_some() {
18881                return None;
18882            }
18883            candidate = Some(current);
18884            continue;
18885        }
18886        let mut cursor = current.walk();
18887        stack.extend(current.named_children(&mut cursor));
18888    }
18889    candidate
18890}
18891
18892fn cpp_is_indexable_item_kind(kind: &str) -> bool {
18893    matches!(
18894        kind,
18895        "namespace_definition"
18896            | "class_specifier"
18897            | "struct_specifier"
18898            | "union_specifier"
18899            | "enum_specifier"
18900            | "function_definition"
18901            | "template_declaration"
18902            | "declaration"
18903            | "field_declaration"
18904            | "alias_declaration"
18905            | "static_assert_declaration"
18906            | "type_definition"
18907            | "using_declaration"
18908            | "linkage_specification"
18909            | "preproc_def"
18910            | "preproc_function_def"
18911            | "preproc_include"
18912            | "preproc_if"
18913            | "preproc_ifdef"
18914            | "preproc_call"
18915    )
18916}
18917
18918#[cfg(test)]
18919mod tests {
18920    use super::*;
18921    use crate::adapter::parse_cpp_file;
18922    use brokk_bifrost_core::analyzer::parsed_file::{
18923        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
18924        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
18925    };
18926    use std::fmt::Write;
18927
18928    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
18929        let mut parser = tree_sitter::Parser::new();
18930        parser
18931            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18932            .unwrap();
18933        let tree = parser.parse(source, None).unwrap();
18934        let file = ProjectFile::new(std::env::temp_dir(), name);
18935        parse_cpp_file(&file, source, &tree)
18936    }
18937
18938    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
18939    /// exactly the spelling it was given, with the nested-class `$` recorded as
18940    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
18941    #[test]
18942    fn rendered_name_fq_round_trips_nested_class_spellings() {
18943        assert!(cpp_rendered_name_fq("").is_empty());
18944        for rendered in [
18945            "Only",
18946            "Top$Only",
18947            "ns.Outer$Inner",
18948            "Top$Only.b",
18949            "ns.Outer$Inner.a",
18950            "Buck$Stop",
18951        ] {
18952            let fq = cpp_rendered_name_fq(rendered);
18953            assert_eq!(
18954                fq.display_native(Language::Cpp, segment_interner()),
18955                rendered,
18956                "native round trip for {rendered}"
18957            );
18958        }
18959        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
18960            .segments()
18961            .iter()
18962            .map(|segment| segment_interner().resolve(*segment).1)
18963            .collect();
18964        assert_eq!(
18965            kinds,
18966            [
18967                SegmentKind::Unknown,
18968                SegmentKind::Unknown,
18969                SegmentKind::Nested
18970            ]
18971        );
18972    }
18973
18974    /// #3631: a `::`-only source spelling must name the nested `$` identity
18975    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
18976    /// the single rendered-name parse.
18977    #[test]
18978    fn colon_source_aliases_name_nested_and_namespace_identities() {
18979        let native = |rendered: &str| {
18980            cpp_colon_source_aliases(rendered)
18981                .into_iter()
18982                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
18983                .collect::<Vec<_>>()
18984        };
18985        let top = native("Top::Only");
18986        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
18987        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
18988        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
18989        let nested = native("ns::Outer::Inner");
18990        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
18991        assert!(
18992            nested.contains(&"ns::Outer.Inner".to_string()),
18993            "{nested:?}"
18994        );
18995        let member = native("Top::Only::b");
18996        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
18997        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
18998        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
18999        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
19000        assert!(cpp_colon_source_aliases("Only").is_empty());
19001        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
19002            fq.segments()
19003                .iter()
19004                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
19005        }));
19006    }
19007
19008    #[test]
19009    fn displaced_terminator_ignores_nested_initializer_endif() {
19010        let source = "#ifdef ENABLE_ITEMS\n\
19011struct Item { int reg; };\n\
19012static const struct Item items[] = {{1}};\n\
19013static const struct Item extras[] = {\n\
19014  {0},\n\
19015#ifndef REDUCED\n\
19016  {1},\n\
19017#endif\n\
19018};\n\
19019int read_item(int i) { return items[i].reg; }\n\
19020#endif\n";
19021        let mut parser = tree_sitter::Parser::new();
19022        parser
19023            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19024            .expect("C++ grammar");
19025        let tree = parser.parse(source, None).expect("fixture tree");
19026        let conditional = tree
19027            .root_node()
19028            .named_child(0)
19029            .filter(|node| node.kind() == "preproc_ifdef")
19030            .expect("outer conditional");
19031
19032        assert!(conditional.has_error());
19033        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
19034        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
19035    }
19036
19037    #[test]
19038    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
19039        let source = "struct Image { PyObject_HEAD Imaging image; };";
19040        let parsed = parse_cpp_declarations(source, "image.h");
19041        let names = parsed
19042            .declarations()
19043            .iter()
19044            .map(|unit| unit.fq_name())
19045            .collect::<Vec<_>>();
19046
19047        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
19048        assert!(
19049            names.iter().all(|name| name != "Image.Imaging"),
19050            "the pseudo-declarator must not become a field: {names:#?}"
19051        );
19052
19053        let pointer = parse_cpp_declarations(
19054            "struct Image { PyObject_HEAD Imaging *image; };",
19055            "image-pointer.h",
19056        );
19057        let pointer_names = pointer
19058            .declarations()
19059            .iter()
19060            .map(|unit| unit.fq_name())
19061            .collect::<Vec<_>>();
19062        assert!(
19063            pointer_names.iter().any(|name| name == "Image.image"),
19064            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
19065        );
19066        assert!(
19067            pointer_names.iter().all(|name| name != "Image.Imaging"),
19068            "the pointer recovery error must not become a field: {pointer_names:#?}"
19069        );
19070
19071        let unrelated_macro = parse_cpp_declarations(
19072            "struct Image { OTHER_HEAD Imaging other; };",
19073            "image-near-miss.h",
19074        );
19075        let unrelated_names = unrelated_macro
19076            .declarations()
19077            .iter()
19078            .map(|unit| unit.fq_name())
19079            .collect::<Vec<_>>();
19080        assert!(
19081            unrelated_names.iter().all(|name| name != "Image.other"),
19082            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
19083        );
19084    }
19085
19086    #[test]
19087    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
19088        let source = r#"namespace testing {
19089namespace internal {
19090    namespace detail {
19091        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
19092        public:
19093            int value() const { return count_ + 1; }
19094        private:
19095            int count_;
19096        };
19097        class GTEST_API_ [[nodiscard]] OtherReporter {
19098        public:
19099            int value() const { return count_ + 2; }
19100        private:
19101            int count_;
19102        };
19103    }
19104
19105    template <typename T>
19106    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
19107
19108    class TailReporter {};
19109}
19110}
19111"#;
19112        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
19113        let declarations = parsed.declarations();
19114        let mut parser = tree_sitter::Parser::new();
19115        parser
19116            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19117            .unwrap();
19118        let tree = parser.parse(source, None).unwrap();
19119        let tail_start = source.find("TailReporter").expect("tail class");
19120        let tail_node = tree
19121            .root_node()
19122            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
19123            .expect("tail class AST node");
19124        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
19125        assert!(
19126            tree.root_node().has_error(),
19127            "the malformed class must exercise recovery"
19128        );
19129        assert!(index.region_at(tail_start).is_some());
19130        assert_eq!(
19131            index.enclosing_namespace_components(tail_node, source),
19132            ["testing", "internal"]
19133        );
19134        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
19135        let mut recovered_parsed = ParsedFile::new(String::new());
19136        let class_unit = CodeUnit::new_fq(
19137            file.clone(),
19138            CodeUnitType::Class,
19139            "testing",
19140            "ScopedFakeTestPartResultReporter",
19141            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
19142        );
19143        let scope = ScopeInfo {
19144            package_name: "testing".to_string(),
19145            module: None,
19146            class_unit: Some(class_unit),
19147            template_signature: Some("<typename T>".to_string()),
19148            template_metadata: Some(CppTemplateMetadata {
19149                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
19150                primary_fq_name: String::new(),
19151                parameters: Vec::new(),
19152                specialization_arguments: Vec::new(),
19153                alias_target: None,
19154            }),
19155            declarations_are_fields: true,
19156            recovered_specialization_member_scope: true,
19157            visible_using_namespaces: Vec::new(),
19158        };
19159        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
19160        let properties = RefCell::new(HashMap::default());
19161        let dependencies =
19162            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
19163        let ancestry = ParentIndex::new(tree.root_node());
19164        let mut visitor = CppVisitor {
19165            file: &file,
19166            source,
19167            parsed: &mut recovered_parsed,
19168            source_collector: &collector,
19169            source_properties: &properties,
19170            source_dependencies: &dependencies,
19171            primary_ancestry: &ancestry,
19172            primary_root_id: tree.root_node().id(),
19173            source_links: HashMap::default(),
19174            source_declarations: HashSet::default(),
19175            active_source_context: None,
19176            c_tag_semantics: false,
19177            c_tag_scope_witness: false,
19178            recovered_class_sibling_scopes: HashMap::default(),
19179            consumed_fragment_regions: Vec::new(),
19180            orphaned_namespaces: &index,
19181            partitioned_regions: Vec::new(),
19182            namespace_forward_scans: HashMap::default(),
19183            field_owners: None,
19184            recovery_captures: Vec::new(),
19185            object_macro_fields: HashMap::default(),
19186            ambiguous_object_macro_fields: HashSet::default(),
19187        };
19188        let recovered = visitor
19189            .recovered_namespace_scope(tail_node, &scope)
19190            .expect("the tail must use the stolen namespace scope");
19191        assert_eq!(recovered.package_name, "testing::internal");
19192        assert!(
19193            recovered.class_unit.is_none(),
19194            "recovered namespace declarations cannot retain the malformed class owner"
19195        );
19196        assert!(recovered.template_signature.is_none());
19197        assert!(recovered.template_metadata.is_none());
19198        assert!(!recovered.declarations_are_fields);
19199        assert!(!recovered.recovered_specialization_member_scope);
19200        assert!(
19201            declarations
19202                .iter()
19203                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
19204            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
19205        );
19206        assert!(
19207            declarations
19208                .iter()
19209                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
19210            "the recovered free function remains in its namespace: {declarations:#?}"
19211        );
19212        assert!(
19213            declarations
19214                .iter()
19215                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
19216            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
19217        );
19218        assert!(
19219            declarations.iter().all(|unit| {
19220                !unit
19221                    .short_name()
19222                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
19223            }),
19224            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
19225        );
19226        assert!(
19227            declarations
19228                .iter()
19229                .all(|unit| !unit.identifier().is_empty()),
19230            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
19231        );
19232    }
19233
19234    #[test]
19235    fn object_like_field_macros_materialize_owner_specific_declarations() {
19236        let source = r#"#define PUBLIC_FIELDS int public_value;
19237#define PRIVATE_FIELDS int private_value;
19238#define NOT_A_FIELD_LIST not a declaration
19239
19240struct First {
19241  PUBLIC_FIELDS
19242  PRIVATE_FIELDS
19243};
19244struct Second {
19245  PUBLIC_FIELDS
19246  NOT_A_FIELD_LIST
19247};
19248#undef PUBLIC_FIELDS
19249struct Third {
19250  PUBLIC_FIELDS
19251};
19252"#;
19253        let parsed = parse_cpp_declarations(source, "macro-fields.c");
19254        let fields = parsed
19255            .declarations()
19256            .iter()
19257            .filter(|unit| unit.is_field())
19258            .map(|unit| unit.fq_name())
19259            .collect::<Vec<_>>();
19260
19261        assert!(
19262            fields.contains(&"First.public_value".to_string()),
19263            "{fields:?}"
19264        );
19265        assert!(
19266            fields.contains(&"First.private_value".to_string()),
19267            "{fields:?}"
19268        );
19269        assert!(
19270            fields.contains(&"Second.public_value".to_string()),
19271            "{fields:?}"
19272        );
19273        assert!(
19274            !fields.iter().any(|field| field.contains("not_a_field")),
19275            "malformed macro must fail closed: {fields:?}"
19276        );
19277        assert!(
19278            !fields.iter().any(|field| field.starts_with("Third.")),
19279            "undefined macro must fail closed: {fields:?}"
19280        );
19281    }
19282
19283    #[test]
19284    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
19285        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
19286        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
19287        let mut macros = parsed
19288            .declarations()
19289            .iter()
19290            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
19291            .collect::<Vec<_>>();
19292        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
19293
19294        assert_eq!(macros.len(), 2, "{macros:#?}");
19295        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
19296        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
19297        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
19298        assert_eq!(
19299            parsed.declaration_ranges(macros[1])[0].start_byte,
19300            source.rfind("#define VALUE 2").expect("second definition")
19301        );
19302    }
19303
19304    #[test]
19305    fn identifies_export_macro_class_base_displaced_into_declarator() {
19306        let source = r#"#define PROJECT_API_
19307namespace project {
19308namespace internal {
19309template <typename T>
19310class Base {};
19311}
19312template <typename T>
19313class Wrapper;
19314template <>
19315class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
19316}
19317"#;
19318        let mut parser = tree_sitter::Parser::new();
19319        parser
19320            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19321            .unwrap();
19322        let tree = parser.parse(source, None).unwrap();
19323        let start = source.find("internal::Base<int>").expect("base");
19324        let mut base = tree
19325            .root_node()
19326            .descendant_for_byte_range(start, start + 8)
19327            .expect("base syntax");
19328        while base.kind() != "qualified_identifier" {
19329            base = base.parent().expect("qualified base ancestor");
19330        }
19331        assert!(
19332            is_recovered_exported_class_base_type_node(base, source),
19333            "{}",
19334            tree.root_node().to_sexp()
19335        );
19336    }
19337
19338    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
19339        let mut identities = parsed
19340            .declarations()
19341            .iter()
19342            .filter(|unit| unit.is_function())
19343            .map(|unit| {
19344                (
19345                    unit.fq_name(),
19346                    unit.signature().unwrap_or_default().to_string(),
19347                )
19348            })
19349            .collect::<Vec<_>>();
19350        identities.sort();
19351        identities
19352    }
19353
19354    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
19355    /// prototype macros. Each case is parsed independently so a preceding
19356    /// malformed line cannot flatten the next declaration's fields into one
19357    /// cascading `ERROR`; that shape has no structural proof and must fail
19358    /// closed rather than fall back to a token scan. Parameter names are
19359    /// dropped from signatures just as they are for ordinary C prototypes.
19360    #[test]
19361    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
19362        let cases = [
19363            (
19364                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
19365                "pg_typemap_fit_to_result",
19366                "(VALUE, VALUE)",
19367            ),
19368            (
19369                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
19370                "pg_typemap_result_value",
19371                "(t_typemap *, VALUE, int, int)",
19372            ),
19373            (
19374                "void pg_typemap_mark _(( void * ));",
19375                "pg_typemap_mark",
19376                "(void *)",
19377            ),
19378            (
19379                "void init_pg_type_map _(( void ));",
19380                "init_pg_type_map",
19381                "(void)",
19382            ),
19383            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
19384            (
19385                "extern VALUE pg_extern _(( VALUE, VALUE ));",
19386                "pg_extern",
19387                "(VALUE, VALUE)",
19388            ),
19389            (
19390                "size_t pg_typemap_memsize _(( const void * ));",
19391                "pg_typemap_memsize",
19392                "(const void *)",
19393            ),
19394            (
19395                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
19396                "pg_wrap_socket_io",
19397                "(int, VALUE, VALUE *, int *)",
19398            ),
19399            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
19400            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
19401            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
19402        ];
19403
19404        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
19405            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
19406            assert_eq!(
19407                function_identities(&parsed),
19408                vec![(name.to_string(), signature.to_string())],
19409                "{source}: {:#?}",
19410                parsed.declarations()
19411            );
19412            assert!(
19413                parsed.declarations().iter().all(|unit| !unit.is_field()),
19414                "{source} must not retain the malformed field: {:#?}",
19415                parsed.declarations()
19416            );
19417        }
19418    }
19419
19420    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
19421    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
19422    /// it as a multiplication of a type by a call
19423    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
19424    /// wrapped in an `expression_statement` that `has_error()` only because
19425    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
19426    /// here with a clean preceding field so the prototype remains the
19427    /// parser-owned `expression_statement` this recovery requires.
19428    #[test]
19429    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
19430        let cases = [
19431            (
19432                "PGconn *pg_get_pgconn _(( VALUE ));",
19433                "pg_get_pgconn",
19434                "(VALUE)",
19435            ),
19436            (
19437                "PGresult* pgresult_get _(( VALUE ));",
19438                "pgresult_get",
19439                "(VALUE)",
19440            ),
19441        ];
19442        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
19443            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
19444            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
19445            assert_eq!(
19446                function_identities(&parsed),
19447                vec![(name.to_string(), signature.to_string())],
19448                "{source}: {:#?}",
19449                parsed.declarations()
19450            );
19451            assert_eq!(
19452                parsed
19453                    .declarations()
19454                    .iter()
19455                    .filter(|unit| unit.is_field())
19456                    .map(|unit| unit.fq_name())
19457                    .collect::<Vec<_>>(),
19458                vec!["rb_mPG".to_string()],
19459                "{source}: {:#?}",
19460                parsed.declarations()
19461            );
19462        }
19463    }
19464
19465    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
19466    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
19467    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
19468    /// regions. The witnessed declaration must still become a Function and
19469    /// its type must not survive as the name of a bogus Field. This test does
19470    /// not claim that structurally flattened neighboring prototypes are safe
19471    /// to recover.
19472    #[test]
19473    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
19474        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
19475VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
19476int pg_typemap_fit_to_copy_get                         _(( VALUE ));
19477VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
19478t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
19479VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
19480void pg_typemap_mark                                   _(( void * ));
19481size_t pg_typemap_memsize                              _(( const void * ));
19482void pg_typemap_compact                                _(( void * ));
19483
19484PGconn *pg_get_pgconn                                  _(( VALUE ));
19485t_pg_connection *pg_get_connection                     _(( VALUE ));
19486VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
19487#ifdef __GNUC__
19488__attribute__((format(printf, 3, 4)))
19489#endif
19490NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
19491VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
19492void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
19493
19494
19495VALUE pg_new_result                                    _(( PGresult *, VALUE ));
19496VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
19497PGresult* pgresult_get                                 _(( VALUE ));
19498VALUE pg_result_check                                  _(( VALUE ));
19499VALUE pg_result_clear                                  _(( VALUE ));
19500VALUE pg_tuple_new                                     _(( VALUE, int ));
19501
19502/*
19503 * Fetch the data pointer for the result object
19504 */
19505static inline t_pg_result *
19506pgresult_get_this( VALUE self )
19507{
19508	return RTYPEDDATA_DATA(self);
19509}
19510
19511
19512rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
19513const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
19514rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
19515
19516"#;
19517        let parsed = parse_cpp_declarations(source, "pg.h");
19518        assert!(
19519            function_identities(&parsed)
19520                .iter()
19521                .any(|(name, signature)| name == "pg_typemap_result_value"
19522                    && signature == "(t_typemap *, VALUE, int, int)"),
19523            "the real issue witness must be a Function with its C signature: {:#?}",
19524            parsed.declarations()
19525        );
19526        assert!(
19527            parsed
19528                .declarations()
19529                .iter()
19530                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
19531            "the issue witness must not leave its return type as a Field name: {:#?}",
19532            parsed.declarations()
19533        );
19534    }
19535
19536    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
19537    /// between `explicit` and a constructor name onto the name, producing a
19538    /// `qualified_identifier` whose `::` it had to invent. The declared member
19539    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
19540    /// macro in type position, was already recovered and is the control that
19541    /// both spellings agree.
19542    #[test]
19543    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
19544        let source = r#"class SIMD_4x26 final {
19545   public:
19546      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
19547      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
19548      int m_v;
19549};
19550"#;
19551        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
19552        assert_eq!(
19553            function_identities(&parsed),
19554            vec![
19555                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
19556                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
19557            ],
19558            "{:#?}",
19559            parsed.declarations()
19560        );
19561    }
19562
19563    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
19564    /// `ERROR` holding the wrapped declaration and every declaration after it
19565    /// until the parser recovers. All of them must be indexed, each with the
19566    /// byte range that spells it.
19567    #[test]
19568    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
19569        let source = r#"#include <cstdint>
19570struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
19571    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
19572                     struct llama_model * model,
19573              struct llama_context_params   params),
19574            "use llama_init_from_model instead");
19575    LLAMA_API int32_t llama_tokenize(
19576        const struct llama_vocab * vocab,
19577                      const char * text,
19578                            bool   parse_special);
19579    LLAMA_API int32_t llama_other(int a);
19580"#;
19581        let parsed = parse_cpp_declarations(source, "llama.h");
19582        assert_eq!(
19583            function_identities(&parsed),
19584            vec![
19585                (
19586                    "llama_new_context_with_model".to_string(),
19587                    "(struct llama_model *, struct llama_context_params)".to_string()
19588                ),
19589                ("llama_other".to_string(), "(int)".to_string()),
19590                (
19591                    "llama_tokenize".to_string(),
19592                    "(const struct llama_vocab *, const char *, bool)".to_string()
19593                ),
19594            ],
19595            "{:#?}",
19596            parsed.declarations()
19597        );
19598
19599        // Each recovered declaration owns the source that spells it, so
19600        // navigation lands on the declaration and not on the macro envelope.
19601        for (name, expected) in [
19602            (
19603                "llama_new_context_with_model",
19604                "LLAMA_API struct llama_context * llama_new_context_with_model(",
19605            ),
19606            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
19607            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
19608        ] {
19609            let unit = parsed
19610                .declarations()
19611                .iter()
19612                .find(|unit| unit.is_function() && unit.fq_name() == name)
19613                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
19614            let [range] = parsed.declaration_ranges(unit) else {
19615                panic!("{name} must have exactly one range");
19616            };
19617            let text = &source[range.start_byte..range.end_byte];
19618            assert!(
19619                text.starts_with(expected),
19620                "{name} range is {text:?}, expected it to start with {expected:?}"
19621            );
19622            assert!(
19623                text.ends_with(')') || text.ends_with(';'),
19624                "{name}: {text:?}"
19625            );
19626        }
19627    }
19628
19629    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
19630    /// invocation's `(` at the end of its line, and the parser then keeps no
19631    /// invocation whole. It hands the first one's hint and its own `)` and `;`
19632    /// to the `expression_statement` beside it, and flattens the ones after
19633    /// that into bare `identifier`/`type_identifier`, `(`,
19634    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
19635    /// already inside. No part of that is a head the grouping can be read from,
19636    /// which is why the invocation is read from the token order instead
19637    /// (#3094).
19638    ///
19639    /// The identifiers are `library_*` at whisper's own lengths on purpose:
19640    /// tree-sitter prices error recovery partly by how much input a repair
19641    /// skips, and the same construct written with shorter names parses cleanly.
19642    #[test]
19643    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
19644        let source = r#"    LIBRARY_DEPRECATED(
19645        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
19646        "use library_init_from_file_with_params instead"
19647    );
19648    LIBRARY_DEPRECATED(
19649        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
19650        "use library_init_from_buffer_with_params instead"
19651    );
19652    LIBRARY_DEPRECATED(
19653        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
19654        "use library_init_with_params instead"
19655    );
19656"#;
19657        let mut parser = tree_sitter::Parser::new();
19658        parser
19659            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19660            .expect("C++ grammar");
19661        let tree = parser.parse(source, None).expect("C++ tree");
19662        let root = tree.root_node();
19663
19664        // The shape this recovery exists for: the second invocation is not a
19665        // node of its own, and the third is a run of bare siblings inside the
19666        // second one's `ERROR`.
19667        let mut cursor = root.walk();
19668        let items = root.children(&mut cursor).collect::<Vec<_>>();
19669        let [first, hint_statement, rest @ ..] = items.as_slice() else {
19670            panic!("{}", root.to_sexp());
19671        };
19672        assert_eq!(first.kind(), "ERROR");
19673        assert_eq!(hint_statement.kind(), "expression_statement");
19674        assert!(
19675            node_text(*first, source).ends_with(','),
19676            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
19677            root.to_sexp()
19678        );
19679        let swallowing = rest
19680            .iter()
19681            .find(|item| item.kind() == "ERROR")
19682            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
19683        let mut swallowing_cursor = swallowing.walk();
19684        let flattened = swallowing
19685            .children(&mut swallowing_cursor)
19686            .map(|child| child.kind())
19687            .collect::<Vec<_>>();
19688        assert_eq!(
19689            flattened,
19690            vec![
19691                "identifier",
19692                "(",
19693                "parameter_declaration",
19694                ",",
19695                "ERROR",
19696                "type_identifier",
19697                "(",
19698                "parameter_declaration",
19699                ",",
19700                "\"",
19701                "identifier",
19702                "identifier",
19703                "identifier",
19704                "\"",
19705                ")",
19706            ],
19707            "the third invocation must be flattened into the second one's node: {}",
19708            root.to_sexp()
19709        );
19710
19711        // Both are admitted, and each names the byte just past its own `;`.
19712        let ancestry = ParentIndex::new(root);
19713        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
19714            .expect("the first invocation");
19715        assert_eq!(
19716            &source[..first_run.invocation_end],
19717            &source[..source.find("instead\"\n    );").expect("first hint")
19718                + "instead\"\n    );".len()]
19719        );
19720        assert_eq!(
19721            first_run.region_end, first_run.invocation_end,
19722            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
19723        );
19724        let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
19725            .expect("the second invocation");
19726        assert!(
19727            swallowing_run.invocation_end < swallowing.end_byte(),
19728            "the second invocation swallowed the third"
19729        );
19730        assert_eq!(
19731            swallowing_run.region_end,
19732            root.end_byte(),
19733            "a swallowing invocation owns the region to the close of its declaration scope"
19734        );
19735
19736        let parsed = parse_cpp_declarations(source, "library.h");
19737        assert_eq!(
19738            function_identities(&parsed),
19739            vec![
19740                (
19741                    "library_init".to_string(),
19742                    "(struct library_model_loader *)".to_string()
19743                ),
19744                (
19745                    "library_init_from_buffer".to_string(),
19746                    "(void *, size_t)".to_string()
19747                ),
19748                (
19749                    "library_init_from_file".to_string(),
19750                    "(const char *)".to_string()
19751                ),
19752            ],
19753            "{:#?}",
19754            parsed.declarations()
19755        );
19756    }
19757
19758    /// The hand-off that keeps the scan finite. An invocation that reaches the
19759    /// end of its own declaration scope swallowed nothing, and the ordinary
19760    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
19761    /// that region for every invocation it finds, so admitting it here would
19762    /// hand the scan its own input back.
19763    #[test]
19764    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
19765        let source = r#"LIBRARY_DEPRECATED(
19766        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
19767        "use library_init_from_file_with_params instead"
19768    );"#;
19769        let mut parser = tree_sitter::Parser::new();
19770        parser
19771            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19772            .expect("C++ grammar");
19773        let tree = parser.parse(source, None).expect("C++ tree");
19774        let root = tree.root_node();
19775        let head = root.named_child(0).expect("the invocation");
19776        let ancestry = ParentIndex::new(root);
19777        assert!(
19778            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
19779            "{}",
19780            root.to_sexp()
19781        );
19782        assert!(
19783            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
19784            "the ordinary reader must be the one that has it: {}",
19785            root.to_sexp()
19786        );
19787        // The index is a speed substitution, never a semantic one: the
19788        // unindexed path is `Node::parent` itself and must agree.
19789        let unindexed = ParentIndex::unindexed();
19790        assert!(
19791            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
19792            "indexed and unindexed climbs must agree"
19793        );
19794        assert_eq!(
19795            macro_wrapped_declarations(head, source, &ancestry).len(),
19796            macro_wrapped_declarations(head, source, &unindexed).len(),
19797            "indexed and unindexed climbs must agree"
19798        );
19799    }
19800
19801    /// Negative controls for the same recovery: a macro invocation whose
19802    /// arguments are not a declaration recovers nothing, whether the parser
19803    /// keeps it clean, reads the arguments as a type, or reads them as a bare
19804    /// declarator.
19805    #[test]
19806    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
19807        for source in [
19808            "int before;\nFOO(1, 2);\nint after;\n",
19809            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
19810            "int before;\nMACRO(int a, int b);\nint after;\n",
19811            "DECLARE_HANDLE(HWND);\nint after;\n",
19812        ] {
19813            let parsed = parse_cpp_declarations(source, "macro-call.h");
19814            assert_eq!(
19815                function_identities(&parsed),
19816                Vec::new(),
19817                "{source:?} must declare no function: {:#?}",
19818                parsed.declarations()
19819            );
19820        }
19821    }
19822
19823    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
19824    /// makes tree-sitter read the macro as the member's type and its argument
19825    /// list as a parenthesized declarator, which then swallows the attributed
19826    /// member and the member written after it. Both are members.
19827    #[test]
19828    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
19829        let source = r#"#include <string_view>
19830namespace Botan {
19831class DL_Group final {
19832   public:
19833      DL_Group() = default;
19834      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
19835      DL_Group(std::string_view pem, int format);
19836      size_t get_p() const;
19837};
19838}
19839"#;
19840        let parsed = parse_cpp_declarations(source, "dl_group.h");
19841        assert_eq!(
19842            function_identities(&parsed),
19843            vec![
19844                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
19845                (
19846                    "Botan.DL_Group.DL_Group".to_string(),
19847                    "(std::string_view)".to_string()
19848                ),
19849                (
19850                    "Botan.DL_Group.DL_Group".to_string(),
19851                    "(std::string_view, int)".to_string()
19852                ),
19853                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
19854            ],
19855            "{:#?}",
19856            parsed.declarations()
19857        );
19858    }
19859
19860    /// #2552 shape 3, export-macro class. The class head macro makes the body a
19861    /// `compound_statement`, so members come from the region reparse, and one
19862    /// string-attribute member used to make that reparse unindexable -- which
19863    /// left the class with no members at all.
19864    #[test]
19865    fn an_export_macro_class_keeps_its_string_attribute_members() {
19866        let source = r#"#include <string_view>
19867namespace Botan {
19868class BOTAN_PUBLIC_API(2, 0) DL_Group final {
19869   public:
19870      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
19871      DL_Group(std::string_view pem, int format);
19872      size_t get_p() const;
19873};
19874}
19875"#;
19876        let parsed = parse_cpp_declarations(source, "dl_group.h");
19877        assert!(
19878            parsed
19879                .declarations()
19880                .iter()
19881                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
19882            "{:#?}",
19883            parsed.declarations()
19884        );
19885        assert_eq!(
19886            function_identities(&parsed),
19887            vec![
19888                (
19889                    "Botan.DL_Group.DL_Group".to_string(),
19890                    "(std::string_view)".to_string()
19891                ),
19892                (
19893                    "Botan.DL_Group.DL_Group".to_string(),
19894                    "(std::string_view, int)".to_string()
19895                ),
19896                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
19897            ],
19898            "{:#?}",
19899            parsed.declarations()
19900        );
19901    }
19902
19903    /// #2552 shape 3, the `= default` variant plus the access-label
19904    /// constructor. The attributed defaulted constructor separates cleanly, but
19905    /// it strands the next member in a bare `ERROR`, and a constructor written
19906    /// with a member-initializer list under `private:` dissolves into
19907    /// expression soup that the reparse recovers from its own source range.
19908    #[test]
19909    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
19910        let source = r#"namespace Botan {
19911class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
19912   public:
19913      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
19914      XMSS_Parameters(int oid, int len);
19915      size_t len() const;
19916
19917   private:
19918      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
19919            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
19920
19921      int m_oid;
19922      int m_wots_oid;
19923      size_t m_element_size;
19924      size_t m_tree_height;
19925};
19926}
19927"#;
19928        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
19929        let constructors = function_identities(&parsed)
19930            .into_iter()
19931            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
19932            .map(|(_, signature)| signature)
19933            .collect::<Vec<_>>();
19934        assert_eq!(
19935            constructors,
19936            vec![
19937                "()".to_string(),
19938                "(int, int)".to_string(),
19939                "(int, int, size_t, size_t)".to_string(),
19940            ],
19941            "{:#?}",
19942            parsed.declarations()
19943        );
19944    }
19945
19946    /// Negative control for the same rule: a real qualified name spells its
19947    /// `::` in the source, so the separator is present rather than MISSING and
19948    /// the out-of-line definition keeps its owner.
19949    #[test]
19950    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
19951        let source = r#"namespace shell {
19952struct Outer {
19953   struct Inner {
19954      Inner(int v);
19955      void run(int v);
19956   };
19957};
19958Outer::Inner::Inner(int v) {}
19959void Outer::Inner::run(int v) {}
19960}
19961"#;
19962        let parsed = parse_cpp_declarations(source, "outer.cpp");
19963        let names = function_identities(&parsed)
19964            .into_iter()
19965            .map(|(fq_name, _)| fq_name)
19966            .collect::<Vec<_>>();
19967        assert!(
19968            names
19969                .iter()
19970                .all(|name| name.starts_with("shell.Outer$Inner.")),
19971            "{names:#?}"
19972        );
19973    }
19974
19975    #[test]
19976    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
19977        let source = r#"namespace control {
19978template <typename T>
19979class AnySpan;
19980template <typename T>
19981class ABSL_ATTRIBUTE_VIEW AnySpan {
19982 public:
19983  int begin() const;
19984};
19985}
19986
19987namespace absl {
19988ABSL_NAMESPACE_BEGIN
19989template <typename T>
19990class ABSL_ATTRIBUTE_VIEW Span {
19991 public:
19992  int begin() const;
19993  int back() const;
19994};
19995
19996int begin();
19997int back();
19998}
19999"#;
20000        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
20001        let declarations = parsed.declarations();
20002        assert!(
20003            declarations
20004                .iter()
20005                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
20006        );
20007        for method in ["begin", "back"] {
20008            assert!(declarations.iter().any(|unit| {
20009                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
20010            }));
20011            assert!(
20012                declarations.iter().any(|unit| {
20013                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
20014                })
20015            );
20016        }
20017        assert!(
20018            declarations
20019                .iter()
20020                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
20021        );
20022        assert!(
20023            declarations
20024                .iter()
20025                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
20026        );
20027        assert!(
20028            declarations
20029                .iter()
20030                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
20031        );
20032    }
20033
20034    #[test]
20035    fn explicit_global_member_definition_has_canonical_package_boundary() {
20036        let source = r#"
20037namespace arangodb::aql {
20038class ExecutionPlan {
20039 public:
20040  template<class... Args> Node* createNode(Args&&... args);
20041};
20042}
20043
20044template<class... Args>
20045Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
20046"#;
20047        let parsed = parse_cpp_declarations(source, "global-member.cpp");
20048
20049        assert!(parsed.declarations().iter().any(|unit| {
20050            unit.is_function()
20051                && unit.package_name() == "arangodb::aql"
20052                && unit.short_name() == "ExecutionPlan.createNode"
20053                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
20054        }));
20055    }
20056
20057    #[test]
20058    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
20059        let source = r#"
20060#ifndef TINYXML2_INCLUDED
20061#define TINYXML2_INCLUDED
20062namespace tinyxml2 {
20063class TINYXML2_LIB XMLUtil {
20064 public:
20065  static const char* SkipWhiteSpace(const char* p) {
20066    while (*p) {
20067      if (*p == ' ') {
20068        ++p;
20069      }
20070    }
20071    return p;
20072  }
20073  static bool StringEqual(const char* p, const char* q) {
20074    return p == q;
20075  }
20076  class TINYXML2_LIB Helper {
20077   public:
20078    void Touch();
20079  };
20080  static void ToStr(int value, char* buffer);
20081 private:
20082  static const char* writeBoolTrue;
20083};
20084
20085class TINYXML2_LIB XMLNode {
20086 public:
20087  virtual XMLNode* ShallowClone() const = 0;
20088  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
20089};
20090}
20091#endif
20092"#;
20093        let mut parser = tree_sitter::Parser::new();
20094        parser
20095            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20096            .unwrap();
20097        let tree = parser.parse(source, None).unwrap();
20098        let mut boundary_found = false;
20099        walk_named_tree_preorder(tree.root_node(), true, |node| {
20100            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
20101                && name == "XMLUtil"
20102            {
20103                boundary_found = fragmented_export_sibling_class_boundary(
20104                    node,
20105                    source,
20106                    &ParentIndex::unindexed(),
20107                )
20108                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
20109                .is_some_and(|(_, name, _, _)| name == "XMLNode");
20110            }
20111            WalkControl::Continue
20112        });
20113        assert!(
20114            boundary_found,
20115            "fixture must exercise the recovered sibling boundary"
20116        );
20117
20118        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
20119        assert!(
20120            parsed
20121                .declarations()
20122                .iter()
20123                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
20124            "{:#?}",
20125            parsed.declarations()
20126        );
20127        assert!(
20128            parsed
20129                .declarations()
20130                .iter()
20131                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
20132            "{:#?}",
20133            parsed.declarations()
20134        );
20135        assert!(parsed.declarations().iter().any(|unit| {
20136            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
20137        }));
20138        assert!(
20139            parsed
20140                .declarations()
20141                .iter()
20142                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
20143        );
20144        assert!(
20145            parsed
20146                .declarations()
20147                .iter()
20148                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
20149        );
20150    }
20151
20152    #[test]
20153    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
20154        // Clang's diagnostic suite intentionally contains this ill-formed
20155        // spelling. The analyzer must retain the parser's explicit-global AST
20156        // boundary instead of constructing `cwg311::::cwg311::X`.
20157        let parsed = parse_cpp_declarations(
20158            r#"
20159namespace cwg311 {
20160namespace X { namespace Y {} }
20161namespace ::cwg311::X {}
20162}
20163"#,
20164            "explicit-global-namespace.cpp",
20165        );
20166
20167        assert!(parsed.declarations().iter().any(|unit| {
20168            unit.kind() == CodeUnitType::Module
20169                && unit.short_name() == "cwg311::X"
20170                && unit.fq_name() == "cwg311::X"
20171        }));
20172        assert!(
20173            parsed
20174                .declarations()
20175                .iter()
20176                .all(|unit| !unit.short_name().contains("::::")),
20177            "recovered namespace names must not retain empty scope components: {:#?}",
20178            parsed.declarations()
20179        );
20180    }
20181
20182    #[test]
20183    fn repeated_scope_separator_does_not_create_empty_function_owner() {
20184        let scope = ScopeInfo {
20185            package_name: "X".to_string(),
20186            module: None,
20187            class_unit: None,
20188            template_signature: None,
20189            template_metadata: None,
20190            declarations_are_fields: false,
20191            recovered_specialization_member_scope: false,
20192            visible_using_namespaces: Vec::new(),
20193        };
20194
20195        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
20196
20197        assert!(owner.is_none());
20198        assert_eq!(name, "doit");
20199        assert_eq!(package, "X");
20200    }
20201
20202    #[test]
20203    fn trailing_decltype_expression_is_not_a_function_declarator() {
20204        let source = r#"
20205namespace boost { namespace detail {
20206#if ! defined(BOOST_NO_SFINAE_EXPR) && \
20207    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
20208    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
20209#define BOOST_THREAD_PROVIDES_INVOKE
20210#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
20211template <class Fp, class A0, class ...Args>
20212inline auto
20213invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
20214       BOOST_THREAD_RV_REF(Args) ...args)
20215    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
20216{
20217    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
20218}
20219#endif
20220#endif
20221}}
20222"#;
20223        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
20224
20225        assert!(
20226            parsed
20227                .declarations()
20228                .iter()
20229                .all(|unit| unit.short_name() != ".*f")
20230        );
20231    }
20232
20233    fn find_class_named<'tree>(
20234        root: Node<'tree>,
20235        source: &str,
20236        expected_name: &str,
20237    ) -> Option<Node<'tree>> {
20238        let mut stack = vec![root];
20239        while let Some(node) = stack.pop() {
20240            if node.kind() == "class_specifier"
20241                && node
20242                    .child_by_field_name("name")
20243                    .is_some_and(|name| node_text(name, source) == expected_name)
20244            {
20245                return Some(node);
20246            }
20247            let mut cursor = node.walk();
20248            stack.extend(node.named_children(&mut cursor));
20249        }
20250        None
20251    }
20252
20253    #[test]
20254    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
20255        let source = r#"EXPORT void definition(struct Value value) {}
20256EXPORT void prototype(struct Value value);
20257"#;
20258        let mut parser = tree_sitter::Parser::new();
20259        parser
20260            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20261            .unwrap();
20262        let tree = parser.parse(source, None).unwrap();
20263        let root = tree.root_node();
20264        let mut cursor = root.walk();
20265        let callables = root
20266            .named_children(&mut cursor)
20267            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
20268            .collect::<Vec<_>>();
20269
20270        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
20271        for callable in callables {
20272            assert!(callable.has_error(), "fixture must exercise error recovery");
20273            assert!(
20274                cpp_sentinel_macro_parts(callable, source).is_none(),
20275                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
20276            );
20277        }
20278    }
20279
20280    #[test]
20281    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
20282        let source = r#"namespace absl {
20283ABSL_NAMESPACE_BEGIN
20284// Generate a floating-point variate conforming to a Beta distribution:
20285template <typename RealType = double>
20286class beta_distribution {
20287 public:
20288  using result_type = RealType;
20289
20290
20291  beta_distribution() : beta_distribution(1) {}
20292
20293  explicit beta_distribution(result_type alpha, result_type beta = 1)
20294      : param_(alpha, beta) {}
20295
20296  explicit beta_distribution(const param_type& p) : param_(p) {}
20297
20298  void reset() {}
20299
20300  // Generating functions
20301  template <typename URBG>
20302  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
20303    return (*this)(g, param_);
20304  }
20305
20306};
20307ABSL_NAMESPACE_END
20308}  // namespace absl
20309"#;
20310        let mut parser = tree_sitter::Parser::new();
20311        parser
20312            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20313            .unwrap();
20314        let tree = parser.parse(source, None).unwrap();
20315        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
20316        let body = namespace
20317            .child_by_field_name("body")
20318            .expect("fixture namespace body");
20319        let sentinel = body.named_child(0).expect("sentinel envelope");
20320        let callable = sentinel
20321            .child_by_field_name("declarator")
20322            .and_then(extract_function_declarator)
20323            .and_then(cpp_function_declarator_name_node)
20324            .expect("preserved callable name");
20325
20326        assert_eq!(sentinel.kind(), "function_definition");
20327        assert_eq!(callable.kind(), "operator_name");
20328        assert!(
20329            cpp_sentinel_macro_parts(sentinel, source).is_some(),
20330            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
20331        );
20332    }
20333
20334    #[test]
20335    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
20336        let source = r#"namespace absl {
20337ABSL_NAMESPACE_BEGIN
20338// absl::discrete_distribution
20339//
20340// A discrete distribution produces random integers i, where 0 <= i < n
20341template <typename IntType = int>
20342class discrete_distribution {
20343 public:
20344  using result_type = IntType;
20345  class param_type {
20346   public:
20347    param_type() { init(); }
20348    template <typename InputIterator>
20349    explicit param_type(InputIterator begin, InputIterator end)
20350        : p_(begin, end) {
20351      init();
20352    }
20353  };
20354  discrete_distribution() : param_() {}
20355  explicit discrete_distribution(const param_type& p) : param_(p) {}
20356};
20357ABSL_NAMESPACE_END
20358}  // namespace absl
20359"#;
20360        let mut parser = tree_sitter::Parser::new();
20361        parser
20362            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20363            .unwrap();
20364        let tree = parser.parse(source, None).unwrap();
20365        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
20366        let body = namespace
20367            .child_by_field_name("body")
20368            .expect("fixture namespace body");
20369        let sentinel = body.named_child(0).expect("sentinel envelope");
20370        let callable = sentinel
20371            .child_by_field_name("declarator")
20372            .and_then(extract_function_declarator)
20373            .and_then(cpp_function_declarator_name_node)
20374            .expect("preserved callable name");
20375
20376        assert_eq!(sentinel.kind(), "function_definition");
20377        assert_eq!(callable.kind(), "identifier");
20378        assert!(
20379            cpp_sentinel_macro_parts(sentinel, source).is_some(),
20380            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
20381        );
20382    }
20383
20384    #[test]
20385    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
20386        let source = r#"
20387#define CPPCHECKLIB
20388class Library {
20389    struct Container {
20390        CPPCHECKLIB static std::string toString(Yield yield);
20391        CPPCHECKLIB static std::string toString(Action action);
20392    };
20393};
20394"#;
20395        let mut parser = tree_sitter::Parser::new();
20396        parser
20397            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20398            .unwrap();
20399        let tree = parser.parse(source, None).unwrap();
20400        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
20401        let parsed = parse_cpp_file(&file, source, &tree);
20402        assert!(
20403            parsed
20404                .declarations()
20405                .iter()
20406                .all(|unit| unit.fq_name() != "Library$Container.std"),
20407            "the qualified return-type namespace must not become a field: {:#?}",
20408            parsed.declarations()
20409        );
20410        for expected in ["(Yield)", "(Action)"] {
20411            assert!(
20412                parsed.declarations().iter().any(|unit| {
20413                    unit.is_function()
20414                        && unit.fq_name() == "Library$Container.toString"
20415                        && unit.signature() == Some(expected)
20416                }),
20417                "recovered toString overload {expected} is missing: {:#?}",
20418                parsed.declarations()
20419            );
20420        }
20421    }
20422
20423    #[test]
20424    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
20425        let source = r#"
20426#define SIMPLECPP_LIB
20427namespace simplecpp {
20428using TokenString = std::string;
20429struct Location { int line{}; };
20430class SIMPLECPP_LIB Token {
20431  TokenString prefix;
20432  void prefix_method() {}
20433 public:
20434  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
20435      whitespaceahead(wsahead), location(loc), string(s)
20436      // The comment must not hide the constructor body from recovery.
20437      {
20438      flags();
20439  }
20440  TokenString string;
20441  bool whitespaceahead;
20442  Location location;
20443  Token *previous{};
20444 private:
20445  void flags() {
20446      whitespaceahead = true;
20447  }
20448};
20449}
20450"#;
20451        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
20452
20453        let location_fields = parsed
20454            .declarations()
20455            .iter()
20456            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
20457            .collect::<Vec<_>>();
20458        assert_eq!(
20459            location_fields.len(),
20460            1,
20461            "location should have one class-owned declaration: {:#?}",
20462            parsed.declarations()
20463        );
20464        assert!(
20465            location_fields[0].is_field(),
20466            "location has wrong kind: {:#?}",
20467            parsed.declarations()
20468        );
20469        assert!(
20470            parsed.declarations().iter().all(|unit| {
20471                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
20472            })
20473        );
20474        assert!(
20475            parsed.declarations().iter().all(|unit| {
20476                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
20477            })
20478        );
20479        assert!(
20480            parsed
20481                .declarations()
20482                .iter()
20483                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
20484        );
20485        assert!(
20486            parsed
20487                .declarations()
20488                .iter()
20489                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
20490            "the recovered class must retain its constructor: {:#?}",
20491            parsed.declarations()
20492        );
20493        assert!(
20494            parsed
20495                .declarations()
20496                .iter()
20497                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
20498        );
20499        assert!(parsed.declarations().iter().any(|unit| {
20500            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
20501        }));
20502        let constructor = parsed
20503            .declarations()
20504            .iter()
20505            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
20506            .expect("recovered constructor");
20507        let constructor_start = source.find("Token(const").expect("constructor start");
20508        let constructor_end = source
20509            .get(
20510                ..source
20511                    .find("  TokenString string;")
20512                    .expect("constructor end"),
20513            )
20514            .expect("constructor slice")
20515            .trim_end()
20516            .len();
20517        assert!(
20518            parsed
20519                .navigation_ranges
20520                .get(constructor)
20521                .is_some_and(|ranges| {
20522                    ranges.iter().any(|range| {
20523                        range.start_byte == constructor_start && range.end_byte == constructor_end
20524                    })
20525                }),
20526            "constructor navigation must span the full body: {:#?}",
20527            parsed.navigation_ranges
20528        );
20529        assert_eq!(
20530            parsed
20531                .signature_metadata
20532                .get(constructor)
20533                .and_then(|metadata| metadata.first())
20534                .and_then(SignatureMetadata::callable_linkage),
20535            Some(CallableLinkage::External)
20536        );
20537        let token_class = parsed
20538            .declarations()
20539            .iter()
20540            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
20541            .expect("recovered Token class");
20542        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
20543        assert!(
20544            parsed
20545                .navigation_ranges
20546                .get(token_class)
20547                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
20548            "class navigation must include the terminating semicolon: {:#?}",
20549            parsed.navigation_ranges
20550        );
20551    }
20552
20553    #[test]
20554    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
20555        let source = r#"
20556#define SIMPLECPP_LIB
20557namespace simplecpp {
20558using TokenString = std::string;
20559class Macro;
20560struct Location {
20561  unsigned int fileIndex{};
20562  unsigned int line{};
20563  unsigned int col{};
20564};
20565struct Output {
20566  int type;
20567};
20568class SIMPLECPP_LIB Token {
20569 public:
20570  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
20571      whitespaceahead(wsahead), location(loc), string(s) {
20572      flags();
20573  }
20574  Token(const Token &tok) :
20575      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
20576      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
20577      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
20578  Token &operator=(const Token &tok) = delete;
20579  const TokenString& str() const { return string; }
20580  void setstr(const std::string &s) { string = s; flags(); }
20581  bool isOneOf(const char ops[]) const;
20582  TokenString macro;
20583  char op;
20584  bool comment;
20585  bool name;
20586  bool number;
20587  bool whitespaceahead;
20588  Location location;
20589  Token *previous{};
20590  Token *next{};
20591 private:
20592  void flags() {
20593      name = !string.empty();
20594      comment = false;
20595      number = false;
20596      op = 0;
20597  }
20598  TokenString string;
20599};
20600}
20601struct Following {
20602  int type;
20603};
20604class SIMPLECPP_LIB Later {
20605 public:
20606  Later(int value) : value(value) {}
20607  int value;
20608};
20609"#;
20610        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
20611        assert!(
20612            parsed
20613                .declarations()
20614                .iter()
20615                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
20616        );
20617        assert!(
20618            !parsed
20619                .declarations()
20620                .iter()
20621                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
20622        );
20623        assert!(
20624            parsed
20625                .declarations()
20626                .iter()
20627                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
20628        );
20629        assert!(
20630            !parsed
20631                .declarations()
20632                .iter()
20633                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
20634        );
20635        assert!(
20636            parsed
20637                .declarations()
20638                .iter()
20639                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
20640        );
20641        assert!(
20642            parsed
20643                .declarations()
20644                .iter()
20645                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
20646        );
20647        assert!(
20648            parsed
20649                .declarations()
20650                .iter()
20651                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
20652        );
20653        assert!(
20654            parsed
20655                .declarations()
20656                .iter()
20657                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
20658        );
20659        assert!(
20660            parsed
20661                .declarations()
20662                .iter()
20663                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
20664        );
20665        assert!(
20666            parsed
20667                .declarations()
20668                .iter()
20669                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
20670        );
20671        assert!(parsed.declarations().iter().all(|unit| {
20672            !matches!(
20673                unit.fq_name().as_str(),
20674                "simplecpp.Token.Following" | "simplecpp.Token.Later"
20675            )
20676        }));
20677        assert!(
20678            !parsed
20679                .declarations()
20680                .iter()
20681                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
20682            "the following struct must remain outside the recovered Token class"
20683        );
20684    }
20685
20686    #[test]
20687    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
20688        let source = r#"
20689#define SIMPLECPP_LIB
20690namespace {
20691namespace simplecpp {
20692using TokenString = std::string;
20693struct Location { int line{}; };
20694class SIMPLECPP_LIB HiddenToken {
20695 public:
20696  HiddenToken(const TokenString &s, const Location &loc) :
20697      location(loc), string(s) {
20698      flags();
20699  }
20700  TokenString string;
20701  Location location;
20702  HiddenToken *previous{};
20703 private:
20704  void flags() {}
20705};
20706}
20707}
20708"#;
20709        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
20710        let constructor = parsed
20711            .declarations()
20712            .iter()
20713            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
20714            .expect("recovered anonymous-namespace constructor");
20715        assert_eq!(
20716            parsed
20717                .signature_metadata
20718                .get(constructor)
20719                .and_then(|metadata| metadata.first())
20720                .and_then(SignatureMetadata::callable_linkage),
20721            Some(CallableLinkage::Internal)
20722        );
20723    }
20724
20725    #[test]
20726    fn macro_qualified_static_field_keeps_real_declarator() {
20727        let source = r#"#define JSON_INLINE_VARIABLE
20728struct Reader {
20729static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
20730static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
20731static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
20732};"#;
20733        let mut parser = tree_sitter::Parser::new();
20734        parser
20735            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20736            .unwrap();
20737        let tree = parser.parse(source, None).unwrap();
20738        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
20739        let parsed = parse_cpp_file(&file, source, &tree);
20740        for expected in [
20741            "Reader.npos",
20742            "Reader.other",
20743            "Reader.pointer",
20744            "Reader.reference",
20745        ] {
20746            assert!(
20747                parsed
20748                    .declarations()
20749                    .iter()
20750                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
20751                "real macro-decorated field {expected} is missing: {:#?}",
20752                parsed.declarations()
20753            );
20754        }
20755        assert!(
20756            parsed
20757                .declarations()
20758                .iter()
20759                .all(|unit| unit.fq_name() != "Reader.std"),
20760            "qualified type prefix became a pseudo-field: {:#?}",
20761            parsed.declarations()
20762        );
20763        let root = tree.root_node();
20764        let mut stack = vec![root];
20765        let mut signatures = Vec::new();
20766        while let Some(current) = stack.pop() {
20767            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
20768            {
20769                signatures.extend(
20770                    declarators
20771                        .into_iter()
20772                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
20773                );
20774            }
20775            let mut cursor = current.walk();
20776            stack.extend(current.named_children(&mut cursor));
20777        }
20778        signatures.sort();
20779        assert_eq!(
20780            signatures,
20781            [
20782                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
20783                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
20784                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
20785                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
20786            ]
20787        );
20788    }
20789
20790    fn member_function_linkage(source: &str) -> CallableLinkage {
20791        let mut parser = tree_sitter::Parser::new();
20792        parser
20793            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20794            .unwrap();
20795        let tree = parser.parse(source, None).unwrap();
20796        let ancestry = ParentIndex::new(tree.root_node());
20797        let mut stack = vec![tree.root_node()];
20798        while let Some(node) = stack.pop() {
20799            if node.kind() == "function_definition" {
20800                let mut current = node.parent();
20801                while let Some(parent) = current {
20802                    if matches!(
20803                        parent.kind(),
20804                        "class_specifier" | "struct_specifier" | "union_specifier"
20805                    ) {
20806                        return cpp_callable_linkage(node, source, &ancestry);
20807                    }
20808                    current = parent.parent();
20809                }
20810            }
20811            let mut cursor = node.walk();
20812            stack.extend(node.named_children(&mut cursor));
20813        }
20814        panic!("fixture has no member function definition");
20815    }
20816
20817    #[test]
20818    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
20819        assert_eq!(
20820            member_function_linkage("struct Named { int method() { return 1; } };"),
20821            CallableLinkage::External
20822        );
20823        assert_eq!(
20824            member_function_linkage(
20825                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
20826            ),
20827            CallableLinkage::Internal
20828        );
20829        assert_eq!(
20830            member_function_linkage("struct { int method() { return 1; } } instance;"),
20831            CallableLinkage::Internal
20832        );
20833        assert_eq!(
20834            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
20835            CallableLinkage::Internal
20836        );
20837    }
20838
20839    #[test]
20840    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
20841        let source = r#"
20842#ifndef PROTON_VALUE_HPP
20843#define PROTON_VALUE_HPP
20844namespace proton {
20845namespace internal {
20846class value_base {
20847  protected:
20848    internal::data& data();
20849    internal::data data_;
20850  friend class codec::encoder;
20851  friend class codec::decoder;
20852};
20853}
20854class value : public internal::value_base, private internal::comparable<value> {
20855  private:
20856    template<class T, class U=void> struct assignable :
20857        public std::enable_if<codec::is_encodable<T>::value, U> {};
20858    template<class U> struct assignable<value, U> {};
20859  public:
20860    PN_CPP_EXTERN value();
20861    PN_CPP_EXTERN value(const value&);
20862    PN_CPP_EXTERN value& operator=(const value&);
20863    PN_CPP_EXTERN value(value&&);
20864    PN_CPP_EXTERN value& operator=(value&&);
20865    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
20866    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
20867        codec::encoder e(*this);
20868        e << x;
20869        return *this;
20870    }
20871    PN_CPP_EXTERN type_id type() const;
20872    PN_CPP_EXTERN bool empty() const;
20873    PN_CPP_EXTERN void clear();
20874    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
20875    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
20876  friend PN_CPP_EXTERN void swap(value&, value&);
20877  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
20878  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
20879  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
20880    value(pn_data_t* d);
20881    void reset(pn_data_t* d = 0);
20882};
20883}
20884#endif
20885"#;
20886        let mut parser = tree_sitter::Parser::new();
20887        parser
20888            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20889            .unwrap();
20890        let tree = parser.parse(source, None).unwrap();
20891        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
20892        let parsed = parse_cpp_file(&file, source, &tree);
20893        let macro_constructors = parsed
20894            .signature_metadata
20895            .iter()
20896            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
20897            .flat_map(|(_, metadata)| metadata)
20898            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
20899            .collect::<Vec<_>>();
20900
20901        assert_eq!(
20902            macro_constructors.len(),
20903            3,
20904            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
20905            parsed.declarations()
20906        );
20907        assert!(
20908            macro_constructors.iter().all(|metadata| {
20909                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
20910            }),
20911            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
20912        );
20913    }
20914
20915    #[test]
20916    fn recovered_export_class_typedef_uses_displaced_alias_name() {
20917        let source = r#"
20918namespace spi {
20919class Filter {
20920public:
20921    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
20922};
20923}
20924namespace filter {
20925class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
20926{
20927public:
20928    typedef spi::Filter BASE_CLASS;
20929    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
20930    BEGIN_LOG4CXX_CAST_MAP()
20931    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
20932    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
20933    END_LOG4CXX_CAST_MAP()
20934    FilterDecision decide() const;
20935};
20936}
20937"#;
20938        let mut parser = tree_sitter::Parser::new();
20939        parser
20940            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20941            .unwrap();
20942        let tree = parser.parse(source, None).unwrap();
20943        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
20944        let parsed = parse_cpp_file(&file, source, &tree);
20945        assert!(
20946            parsed.declarations().iter().any(|unit| {
20947                unit.is_class()
20948                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
20949                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
20950            }),
20951            "the displaced typedef alias must retain its declared name: {:#?}",
20952            parsed.declarations()
20953        );
20954        assert!(
20955            parsed
20956                .declarations()
20957                .iter()
20958                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
20959            "the qualified underlying type must not become a false nested alias: {:#?}",
20960            parsed.declarations()
20961        );
20962    }
20963
20964    #[test]
20965    fn exported_single_base_recovery_uses_displaced_class_name() {
20966        let source = r#"
20967class CORE_EXPORT QgsPoint : public AbstractGeometry
20968{
20969    Q_GADGET
20970
20971    Q_PROPERTY( double x READ x WRITE setX )
20972    Q_PROPERTY( double y READ y WRITE setY )
20973    Q_PROPERTY( double z READ z WRITE setZ )
20974    Q_PROPERTY( double m READ m WRITE setM )
20975
20976  public:
20977#ifndef SIP_RUN
20978    QgsPoint(
20979      double x = std::numeric_limits<double>::quiet_NaN(),
20980      double y = std::numeric_limits<double>::quiet_NaN(),
20981      double z = std::numeric_limits<double>::quiet_NaN(),
20982      double m = std::numeric_limits<double>::quiet_NaN(),
20983      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
20984    );
20985#else
20986    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 )];
20987    % MethodCode
20988    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
20989    {
20990      int state;
20991      sipIsErr = 0;
20992      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
20993      if ( !sipIsErr )
20994      {
20995        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
20996      }
20997      sipReleaseType( p, sipType_QgsPointXY, state );
20998    }
20999    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
21000    {
21001      int state;
21002      sipIsErr = 0;
21003
21004      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
21005      if ( !sipIsErr )
21006      {
21007        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
21008      }
21009      sipReleaseType( p, sipType_QPointF, state );
21010    }
21011    else if (
21012      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
21013      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
21014      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
21015      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
21016    {
21017      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
21018      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
21019      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
21020      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
21021      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
21022      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
21023    }
21024    else // Invalid ctor arguments
21025    {
21026      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
21027      sipIsErr = 1;
21028    }
21029    % End
21030#endif
21031
21032    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
21033    explicit QgsPoint( QPointF p ) SIP_SKIP;
21034    explicit QgsPoint(
21035      Qgis::WkbType wkbType,
21036      double x = std::numeric_limits<double>::quiet_NaN(),
21037      double y = std::numeric_limits<double>::quiet_NaN(),
21038      double z = std::numeric_limits<double>::quiet_NaN(),
21039      double m = std::numeric_limits<double>::quiet_NaN()
21040    ) SIP_SKIP;
21041    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
21042    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
21043    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
21044#ifndef SIP_RUN
21045  private:
21046    bool fuzzyHelper(
21047      double epsilon,
21048      const AbstractGeometry &other,
21049      bool is3DFlag,
21050      bool isMeasureFlag
21051    ) const
21052    {
21053      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
21054    }
21055#endif
21056};
21057class Ordinary : public Base { public: Ordinary(); };
21058class API_EXPORT Plain { public: Plain(); };
21059class API_EXPORT : public Base {};
21060class
21061PN_CPP_CLASS_EXTERN Sender : public Link {
21062    Sender();
21063    struct impl;
21064    struct impl& get_impl() const;
21065};
21066class thread_ctx_t {};
21067class ctx_t ZMQ_FINAL : public thread_ctx_t {
21068    bool start();
21069};
21070"#;
21071        let mut parser = tree_sitter::Parser::new();
21072        parser
21073            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21074            .unwrap();
21075        let tree = parser.parse(source, None).unwrap();
21076        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
21077        let parsed = parse_cpp_file(&file, source, &tree);
21078        let declarations = parsed.declarations();
21079
21080        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
21081            assert!(
21082                declarations
21083                    .iter()
21084                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
21085                "missing recovered class {expected}: {declarations:#?}"
21086            );
21087        }
21088        let qgs_point = declarations
21089            .iter()
21090            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
21091            .expect("recovered QgsPoint class");
21092        assert_eq!(
21093            parsed.raw_supertypes.get(qgs_point),
21094            Some(&vec!["AbstractGeometry".to_string()]),
21095            "single-base export recovery must retain its displaced base"
21096        );
21097        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
21098        assert!(
21099            parsed
21100                .navigation_ranges
21101                .get(qgs_point)
21102                .is_some_and(|ranges| {
21103                    !ranges.is_empty()
21104                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
21105                }),
21106            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
21107            parsed.navigation_ranges.get(qgs_point)
21108        );
21109        let sender = declarations
21110            .iter()
21111            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
21112            .expect("recovered Sender class");
21113        assert_eq!(
21114            parsed.raw_supertypes.get(sender),
21115            Some(&vec!["Link".to_string()]),
21116            "post-declarator export recovery must retain its displaced base"
21117        );
21118        let recovered_member = declarations
21119            .iter()
21120            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
21121            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
21122        assert_eq!(
21123            parsed
21124                .signature_metadata
21125                .get(recovered_member)
21126                .and_then(|metadata| metadata.first())
21127                .and_then(SignatureMetadata::callable_linkage),
21128            Some(CallableLinkage::External),
21129            "a named recovered class's members have external linkage"
21130        );
21131        let ctx = declarations
21132            .iter()
21133            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
21134            .expect("recovered ctx_t class");
21135        assert_eq!(
21136            parsed.raw_supertypes.get(ctx),
21137            Some(&vec!["thread_ctx_t".to_string()]),
21138            "postfix export-macro recovery must retain its displaced base"
21139        );
21140        assert!(
21141            declarations.iter().any(|unit| {
21142                unit.is_function()
21143                    && unit.fq_name() == "QgsPoint.QgsPoint"
21144                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
21145            }),
21146            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
21147        );
21148        assert!(
21149            declarations.iter().all(|unit| {
21150                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
21151            }),
21152            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
21153        );
21154    }
21155
21156    #[test]
21157    fn function_like_export_macro_classes_keep_names_and_base_edges() {
21158        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
21159        // error: a plain body, a single base, `final` without a base, `final`
21160        // with one base, and `final` with a comma-separated base list.
21161        let source = r#"
21162namespace api {
21163class PROJECT_PUBLIC_API(2, 0) Prelude {
21164  public:
21165    Prelude();
21166};
21167class PROJECT_PUBLIC_API(2, 0) Base {
21168  public:
21169    Base(int value);
21170};
21171class PROJECT_PUBLIC_API(2, 0) Mixin {
21172  public:
21173    Mixin();
21174};
21175class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
21176  public:
21177    Adopted(int value);
21178};
21179class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
21180  public:
21181    Derived(int value);
21182};
21183class PROJECT_PUBLIC_API(2, 0) Solo final {
21184  public:
21185    Solo();
21186};
21187class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
21188  public:
21189    Blended(int value);
21190};
21191class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
21192  public:
21193    Woven(int value);
21194};
21195} // namespace api
21196"#;
21197        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
21198        let declarations = parsed.declarations();
21199        let class_named = |name: &str| {
21200            declarations
21201                .iter()
21202                .find(|unit| unit.is_class() && unit.fq_name() == name)
21203                .unwrap_or_else(|| {
21204                    panic!("missing function-like export macro class {name}: {declarations:#?}")
21205                })
21206        };
21207        let base = class_named("api.Base");
21208        class_named("api.Prelude");
21209        class_named("api.Mixin");
21210
21211        assert_eq!(
21212            parsed.raw_supertypes.get(class_named("api.Adopted")),
21213            Some(&vec!["Base".to_string()])
21214        );
21215        assert_eq!(
21216            parsed.raw_supertypes.get(class_named("api.Derived")),
21217            Some(&vec!["Base".to_string()])
21218        );
21219        assert_eq!(
21220            parsed.raw_supertypes.get(class_named("api.Solo")),
21221            None,
21222            "a final class without a base list must not invent a supertype"
21223        );
21224        assert_eq!(
21225            parsed.raw_supertypes.get(class_named("api.Blended")),
21226            Some(&vec!["Base".to_string(), "Mixin".to_string()])
21227        );
21228        assert_eq!(
21229            parsed.raw_supertypes.get(class_named("api.Woven")),
21230            Some(&vec!["Base".to_string(), "Mixin".to_string()])
21231        );
21232        assert!(
21233            declarations
21234                .iter()
21235                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21236            "the export macro must not become a declaration: {declarations:#?}"
21237        );
21238        assert!(
21239            declarations.iter().all(|unit| !matches!(
21240                unit.identifier(),
21241                "final" | "public" | "protected" | "private"
21242            )),
21243            "the head specifiers must not become declarations: {declarations:#?}"
21244        );
21245        assert!(
21246            parsed
21247                .navigation_ranges
21248                .get(base)
21249                .is_some_and(|ranges| !ranges.is_empty()),
21250            "the recovered base must retain a navigable declaration range"
21251        );
21252    }
21253
21254    #[test]
21255    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
21256        // #2557: the class name is the last identifier before the head ends
21257        // (`final`, the base clause `:`, or the body), whatever its spelling.
21258        // A class named in capitals (`X509_CA`) is a class, and an object-like
21259        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
21260        // the control whose spelling never mattered.
21261        let source = r#"
21262namespace api {
21263class PROJECT_PUBLIC_API(2, 0) Base {
21264  public:
21265    Base();
21266};
21267class PROJECT_PUBLIC_API(2, 0) Mixin {
21268  public:
21269    Mixin();
21270};
21271class PROJECT_PUBLIC_API(2, 0) Name {
21272  public:
21273    Name();
21274};
21275class PROJECT_PUBLIC_API(2, 0) X509_CA final {
21276  public:
21277    X509_CA();
21278};
21279class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
21280  public:
21281    HSS_LMS_KEY();
21282};
21283class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
21284  public:
21285    GOST_3410();
21286};
21287class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
21288  public:
21289    PKCS11_RSA();
21290};
21291class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
21292  public:
21293    Plain();
21294};
21295class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
21296  public:
21297    Decorated();
21298};
21299class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
21300  public:
21301    Layered();
21302};
21303} // namespace api
21304"#;
21305        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
21306        let declarations = parsed.declarations();
21307        let class_named = |name: &str| {
21308            declarations
21309                .iter()
21310                .find(|unit| unit.is_class() && unit.fq_name() == name)
21311                .unwrap_or_else(|| {
21312                    panic!("missing function-like export macro class {name}: {declarations:#?}")
21313                })
21314        };
21315        for (name, bases) in [
21316            ("api.Name", None),
21317            ("api.X509_CA", None),
21318            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
21319            ("api.GOST_3410", Some(vec!["Base"])),
21320            ("api.PKCS11_RSA", None),
21321            ("api.Plain", None),
21322            ("api.Decorated", Some(vec!["Base"])),
21323            ("api.Layered", Some(vec!["Base", "Mixin"])),
21324        ] {
21325            let expected =
21326                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
21327            assert_eq!(
21328                parsed.raw_supertypes.get(class_named(name)),
21329                expected.as_ref(),
21330                "{name}"
21331            );
21332        }
21333        assert!(
21334            declarations.iter().all(|unit| !matches!(
21335                unit.identifier(),
21336                "PROJECT_PUBLIC_API"
21337                    | "OTHER_MACRO"
21338                    | "FIRST_MACRO"
21339                    | "SECOND_MACRO"
21340                    | "final"
21341                    | "public"
21342            )),
21343            "macros and head specifiers must not become declarations: {declarations:#?}"
21344        );
21345    }
21346
21347    #[test]
21348    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
21349        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
21350        // head, the body's `{` and its first member in one declaration-scope
21351        // ERROR, scatters the remaining members across the container's
21352        // siblings, and ends the container on the class's own `}`. The class
21353        // was lost and the macro minted a class of its own.
21354        let source = r#"
21355namespace api {
21356
21357/**
21358* Doc comment
21359*/
21360class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
21361   public:
21362      /**
21363      * Construct from a point.
21364      */
21365      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
21366
21367#if defined(PROJECT_HAS_LEGACY_POINT)
21368      /**
21369      * Construct from a legacy point.
21370      */
21371      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
21372#endif
21373
21374      std::string algo_name() const override;
21375
21376      AlgorithmIdentifier algorithm_identifier() const override;
21377};
21378
21379}
21380"#;
21381        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
21382        let declarations = parsed.declarations();
21383        let class = declarations
21384            .iter()
21385            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
21386            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
21387        assert_eq!(
21388            parsed.raw_supertypes.get(class),
21389            Some(&vec!["BaseKey".to_string()]),
21390            "the virtual base is the class's base: {declarations:#?}"
21391        );
21392        for member in [
21393            "api.VirtualBased.algo_name",
21394            "api.VirtualBased.algorithm_identifier",
21395        ] {
21396            assert!(
21397                declarations
21398                    .iter()
21399                    .any(|unit| unit.is_function() && unit.fq_name() == member),
21400                "{member} must be owned by the recovered class: {declarations:#?}"
21401            );
21402        }
21403        assert!(
21404            declarations
21405                .iter()
21406                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21407            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
21408        );
21409    }
21410
21411    #[test]
21412    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
21413        // #2924: object-like macro lines before the head demote the `class`
21414        // keyword to a bare identifier and make the macro invocation an
21415        // `init_declarator`, so the head arrives as a `declaration` rather than
21416        // as an ERROR carrying a `class_specifier`. The name is still the last
21417        // identifier before `final`, and `virtual` is a base specifier rather
21418        // than a base.
21419        let source = r#"
21420namespace api {
21421
21422DIAGNOSTIC_PUSH
21423DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
21424
21425class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
21426                                               public virtual api::Inner::Key {
21427   public:
21428      std::string algo_name() const override;
21429};
21430
21431DIAGNOSTIC_POP
21432
21433}
21434"#;
21435        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
21436        let declarations = parsed.declarations();
21437        let class = declarations
21438            .iter()
21439            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
21440            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
21441        assert_eq!(
21442            parsed.raw_supertypes.get(class),
21443            Some(&vec![
21444                "api::Outer::Key".to_string(),
21445                "api::Inner::Key".to_string()
21446            ]),
21447            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
21448        );
21449        assert!(
21450            declarations
21451                .iter()
21452                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
21453            "the member is owned by the recovered class: {declarations:#?}"
21454        );
21455        assert!(
21456            declarations
21457                .iter()
21458                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21459            "the macro invocation must not mint a declaration: {declarations:#?}"
21460        );
21461    }
21462
21463    #[test]
21464    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
21465        // The class embedded in a preceding malformed body follows the same
21466        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
21467        let fixture = |head: &str, name: &str| {
21468            format!(
21469                r#"
21470namespace api {{
21471class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
21472   public:
21473      /** Return a descriptive string. */
21474      const char* what() const noexcept override {{ return m_msg.c_str(); }}
21475
21476      /** Return the type of error. */
21477      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
21478
21479      /** Return an associated error code. */
21480      virtual int error_code() const noexcept {{ return 0; }}
21481
21482      /** Avoid throwing the base directly. */
21483      explicit Exception(std::string_view msg);
21484
21485      /** Avoid throwing the base directly. */
21486      Exception(const char* prefix, std::string_view msg);
21487
21488      /** Avoid throwing the base directly. */
21489      Exception(std::string_view msg, const std::exception& e);
21490
21491   private:
21492      std::string m_msg;
21493}};
21494
21495class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
21496   public:
21497      explicit {name}(std::string_view msg);
21498
21499      explicit {name}(std::string_view msg, std::string_view where);
21500
21501      {name}(std::string_view msg, const std::exception& e);
21502
21503      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
21504}};
21505}} // namespace api
21506"#
21507            )
21508        };
21509        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
21510            let source = fixture(head, name);
21511            let mut parser = Parser::new();
21512            parser
21513                .set_language(&tree_sitter_cpp::LANGUAGE.into())
21514                .expect("set C++ grammar");
21515            let tree = parser.parse(&source, None).expect("parse fixture");
21516            let mut embedded = Vec::new();
21517            let mut stack = vec![tree.root_node()];
21518            while let Some(node) = stack.pop() {
21519                embedded.extend(
21520                    recover_embedded_function_like_export_classes(node, &source)
21521                        .into_iter()
21522                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
21523                );
21524                let mut cursor = node.walk();
21525                stack.extend(node.named_children(&mut cursor));
21526            }
21527            assert!(
21528                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
21529                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
21530                tree.root_node().to_sexp()
21531            );
21532            assert!(
21533                embedded
21534                    .iter()
21535                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
21536                "{head}: the object-like macro is not a class: {embedded:#?}"
21537            );
21538
21539            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
21540            let declarations = parsed.declarations();
21541            let class = declarations
21542                .iter()
21543                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
21544                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
21545            assert_eq!(
21546                parsed.raw_supertypes.get(class),
21547                Some(&vec!["Exception".to_string()]),
21548                "{head}"
21549            );
21550            assert!(
21551                declarations
21552                    .iter()
21553                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
21554                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
21555            );
21556        }
21557    }
21558
21559    #[test]
21560    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
21561        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
21562        // `class MACRO(3, 6) Name final :` in the error, the first base as the
21563        // `type` of the following declaration, and closes that declaration with
21564        // a zero-width `MISSING identifier`. Reading the head by position must
21565        // not take that missing node for the class name: an empty name panics
21566        // at `FqName` construction, which took the Botan corpus replay down
21567        // (#2557). The head's tokens straddle the two nodes, so the positional
21568        // read spans both and recovers the class rather than leaving the
21569        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
21570        let source = r#"
21571namespace api {
21572class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
21573                                                    public virtual Botan::EC_PublicKey {
21574   public:
21575      std::string algo_name() const override { return "ECDSA"; }
21576};
21577} // namespace api
21578"#;
21579        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
21580        let declarations = parsed.declarations();
21581        assert!(
21582            declarations
21583                .iter()
21584                .all(|unit| !unit.identifier().is_empty()),
21585            "no declaration may carry an empty name: {declarations:#?}"
21586        );
21587        assert!(
21588            declarations
21589                .iter()
21590                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
21591            "macros and head specifiers must not become declarations: {declarations:#?}"
21592        );
21593        let class = declarations
21594            .iter()
21595            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
21596            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
21597        assert_eq!(
21598            parsed.raw_supertypes.get(class),
21599            Some(&vec![
21600                "Botan::TPM2::PublicKey".to_string(),
21601                "Botan::EC_PublicKey".to_string()
21602            ]),
21603            "both qualified virtual bases are bases: {declarations:#?}"
21604        );
21605        let source_facts = parsed.source_facts.as_ref().unwrap();
21606        let declaration = parsed
21607            .source_declaration_units
21608            .iter()
21609            .find(|(_, unit)| unit == class)
21610            .map(|(declaration, _)| *declaration)
21611            .expect("recovered class source bridge");
21612        let bases = source_facts
21613            .cpp
21614            .as_ref()
21615            .unwrap()
21616            .declarations
21617            .iter()
21618            .find(|fact| fact.declaration == declaration)
21619            .expect("recovered class source fact")
21620            .bases
21621            .clone();
21622        assert_eq!(
21623            bases
21624                .iter()
21625                .map(|base| base.components.clone())
21626                .collect::<Vec<_>>(),
21627            vec![
21628                vec!["Botan", "TPM2", "PublicKey"],
21629                vec!["Botan", "EC_PublicKey"],
21630            ],
21631            "generalized recovery must publish structured base components"
21632        );
21633        assert!(
21634            bases.iter().all(|base| base.is_virtual),
21635            "generalized recovery must preserve virtual base qualifiers"
21636        );
21637        assert!(
21638            declarations
21639                .iter()
21640                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
21641            "the head must not mint a macro-named class: {declarations:#?}"
21642        );
21643        let mixed_source = source.replace(
21644            "public virtual Botan::EC_PublicKey",
21645            "public Botan::EC_PublicKey",
21646        );
21647        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
21648        let qualifiers = mixed
21649            .source_facts
21650            .as_ref()
21651            .unwrap()
21652            .cpp
21653            .as_ref()
21654            .unwrap()
21655            .declarations
21656            .iter()
21657            .flat_map(|declaration| &declaration.bases)
21658            .map(|base| base.is_virtual)
21659            .collect::<Vec<_>>();
21660        assert_eq!(
21661            qualifiers,
21662            [true, false],
21663            "base qualifiers must not cross the comma"
21664        );
21665    }
21666
21667    #[test]
21668    fn function_like_export_class_survives_a_preceding_malformed_body() {
21669        let source = r#"
21670namespace api {
21671class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
21672   public:
21673      /** Return a descriptive string. */
21674      const char* what() const noexcept override { return m_msg.c_str(); }
21675
21676      /** Return the type of error. */
21677      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
21678
21679      /** Return an associated error code. */
21680      virtual int error_code() const noexcept { return 0; }
21681
21682      /** Avoid throwing the base directly. */
21683      explicit Exception(std::string_view msg);
21684
21685      /** Avoid throwing the base directly. */
21686      Exception(const char* prefix, std::string_view msg);
21687
21688      /** Avoid throwing the base directly. */
21689      Exception(std::string_view msg, const std::exception& e);
21690
21691   private:
21692      std::string m_msg;
21693};
21694
21695class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
21696   public:
21697      explicit Invalid_Argument(std::string_view msg);
21698
21699      explicit Invalid_Argument(std::string_view msg, std::string_view where);
21700
21701      Invalid_Argument(std::string_view msg, const std::exception& e);
21702
21703      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
21704};
21705} // namespace api
21706"#;
21707        let mut parser = Parser::new();
21708        parser
21709            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21710            .expect("set C++ grammar");
21711        let tree = parser.parse(source, None).expect("parse fixture");
21712        let mut stack = vec![tree.root_node()];
21713        let mut saw_embedded_shape = false;
21714        while let Some(node) = stack.pop() {
21715            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
21716                .iter()
21717                .any(|recovered| recovered.name == "Invalid_Argument");
21718            let mut cursor = node.walk();
21719            stack.extend(node.named_children(&mut cursor));
21720        }
21721        assert!(
21722            saw_embedded_shape,
21723            "fixture must retain the embedded error geometry: {}",
21724            tree.root_node().to_sexp()
21725        );
21726
21727        let parsed = parse_cpp_file(
21728            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
21729            source,
21730            &tree,
21731        );
21732        let declarations = parsed.declarations();
21733        let exception = declarations
21734            .iter()
21735            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
21736            .expect("qualified-base export class");
21737        let invalid = declarations
21738            .iter()
21739            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
21740            .expect("class embedded in the preceding malformed body");
21741
21742        assert_eq!(
21743            parsed.raw_supertypes.get(exception),
21744            Some(&vec!["std::exception".to_string()])
21745        );
21746        assert_eq!(
21747            parsed.raw_supertypes.get(invalid),
21748            Some(&vec!["Exception".to_string()])
21749        );
21750        assert!(
21751            parsed.materialization_records.iter().any(|record| matches!(
21752                record,
21753                MaterializationRecord::RecoveredDeclaration { unit, .. }
21754                    if unit == invalid
21755            )),
21756            "the embedded class must retain recovery provenance: {:#?}",
21757            parsed.materialization_records
21758        );
21759    }
21760
21761    #[test]
21762    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
21763        let source = r#"
21764public:
21765   explicit Lookup_Error(std::string_view err) : Exception(err) {}
21766
21767   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
21768"#;
21769        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
21770            .expect("reparse merged constructor body");
21771        let (range, body) =
21772            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
21773                .unwrap_or_else(|| {
21774                    panic!(
21775                        "the merged constructor must retain its structured declarator/body: {}",
21776                        tree.root_node().to_sexp()
21777                    )
21778                });
21779        assert_eq!(
21780            source.get(range).expect("constructor range"),
21781            "Lookup_Error(std::string_view err) : Exception(err) {}"
21782        );
21783        assert_eq!(node_text(body, source), "{}");
21784    }
21785
21786    #[test]
21787    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
21788        let positive_source =
21789            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
21790        let mut parser = tree_sitter::Parser::new();
21791        parser
21792            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21793            .unwrap();
21794        let positive_tree = parser.parse(positive_source, None).unwrap();
21795        assert!(cpp_reparsed_members_are_indexable(
21796            positive_tree.root_node(),
21797            positive_source
21798        ));
21799
21800        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
21801        let negative_tree = parser.parse(negative_source, None).unwrap();
21802        assert!(!cpp_reparsed_members_are_indexable(
21803            negative_tree.root_node(),
21804            negative_source
21805        ));
21806    }
21807
21808    #[test]
21809    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
21810        let copy_control_source = r#"
21811public:
21812    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
21813    explicit Token(const Token* tok);
21814    ~Token();
21815    Token* astOperand1() { return nullptr; }
21816"#;
21817        let constraint_source = r#"
21818private:
21819    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
21820    static T *tokAtImpl(T *tok, int index) {
21821        return tok;
21822    }
21823
21824    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
21825    static T *linkAtImpl(T *tok, int index) {
21826        return tok;
21827    }
21828
21829public:
21830    int late() const { return 1; }
21831"#;
21832        let mut parser = tree_sitter::Parser::new();
21833        parser
21834            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21835            .unwrap();
21836        let copy_control_tree = parser
21837            .parse(copy_control_source, None)
21838            .expect("parse copy-control fixture");
21839        assert!(
21840            copy_control_tree.root_node().has_error(),
21841            "fixture must exercise adjacent copy-control recovery"
21842        );
21843        assert!(
21844            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
21845            "a complete late getter must remain recoverable after adjacent copy-control declarations"
21846        );
21847        let mut cursor = copy_control_tree.root_node().walk();
21848        assert!(
21849            copy_control_tree
21850                .root_node()
21851                .named_children(&mut cursor)
21852                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
21853            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
21854            copy_control_tree.root_node().to_sexp()
21855        );
21856        let constraint_tree = parser
21857            .parse(constraint_source, None)
21858            .expect("parse constraint-macro fixture");
21859        assert!(constraint_tree.root_node().has_error());
21860        assert!(
21861            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
21862            "complete constraint-macro members must not hide a later ordinary member"
21863        );
21864        let mut cursor = constraint_tree.root_node().walk();
21865        assert!(
21866            constraint_tree
21867                .root_node()
21868                .named_children(&mut cursor)
21869                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
21870                    child,
21871                    constraint_source
21872                )),
21873            "fixture must retain the split constraint-macro prefix/function geometry"
21874        );
21875    }
21876
21877    #[test]
21878    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
21879        let source = r#"
21880struct Analyzer {
21881    struct Action {
21882        Action() = default;
21883        Action(const Action&) = default;
21884        Action& operator=(const Action& rhs) & = default;
21885
21886        template<class T,
21887                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
21888                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
21889        // NOLINTNEXTLINE(google-explicit-constructor)
21890        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
21891        {}
21892
21893        enum : std::uint16_t { None = 0, Read = (1 << 0) };
21894        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
21895
21896    private:
21897        unsigned int mFlag{};
21898    };
21899
21900    enum class Direction : unsigned char { Forward, Reverse };
21901    virtual Action analyze(Direction d) const = 0;
21902};
21903"#;
21904        let mut parser = tree_sitter::Parser::new();
21905        parser
21906            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21907            .unwrap();
21908        let tree = parser.parse(source, None).unwrap();
21909        assert!(tree.root_node().has_error());
21910        let root = tree.root_node();
21911        let outer = root
21912            .named_children(&mut root.walk())
21913            .find(|child| child.kind() == "ERROR")
21914            .expect("fragmented Analyzer prefix");
21915        let outer_recovered =
21916            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
21917        assert_eq!(outer_recovered.name, "Analyzer");
21918        let outer_tree = cpp_reparse_fragmented_class_body(
21919            source,
21920            outer_recovered.body.reparse_start,
21921            outer_recovered.body.reparse_end,
21922        )
21923        .expect("reparse Analyzer body");
21924        let outer_root = outer_tree.root_node();
21925        let action_prefix = outer_root
21926            .named_children(&mut outer_root.walk())
21927            .find(|child| child.kind() == "ERROR")
21928            .expect("fragmented Action prefix");
21929        let action_recovered = fragmented_class_body(action_prefix, source)
21930            .expect("structured Action fragment boundary");
21931        assert_eq!(action_recovered.name, "Action");
21932        let action_tree = cpp_reparse_fragmented_class_body(
21933            source,
21934            action_recovered.body.reparse_start,
21935            action_recovered.body.reparse_end,
21936        )
21937        .expect("reparse Action body");
21938        let action_root = action_tree.root_node();
21939        let macro_prefix = action_root
21940            .named_children(&mut action_root.walk())
21941            .find(|child| child.kind() == "ERROR")
21942            .expect("constraint macro prefix");
21943        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
21944            .expect("structured template macro prefix");
21945        let macro_companion =
21946            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
21947        assert!(
21948            cpp_reparsed_template_macro_constructor_companion_is_indexable(
21949                macro_companion,
21950                macro_parameter,
21951                source,
21952            ),
21953            "split constrained constructor must be admitted: {}",
21954            macro_companion.to_sexp()
21955        );
21956        assert!(
21957            cpp_reparsed_members_are_indexable(action_root, source),
21958            "complete Action body must pass the recovery gate: {}",
21959            action_tree.root_node().to_sexp()
21960        );
21961        assert!(
21962            cpp_reparsed_members_are_indexable(outer_root, source),
21963            "complete Analyzer body must pass the recovery gate: {}",
21964            outer_tree.root_node().to_sexp()
21965        );
21966        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
21967        let parsed = parse_cpp_file(&file, source, &tree);
21968        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
21969            assert!(
21970                parsed
21971                    .declarations()
21972                    .iter()
21973                    .any(|unit| unit.fq_name() == expected),
21974                "missing recovered declaration {expected}: {:#?}",
21975                parsed.declarations()
21976            );
21977        }
21978        assert!(
21979            parsed
21980                .declarations()
21981                .iter()
21982                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
21983            "nested members must not remain flattened: {:#?}",
21984            parsed.declarations()
21985        );
21986    }
21987
21988    #[test]
21989    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
21990        let source = r#"
21991raw_hash_set& operator=(raw_hash_set&& that) {
21992  return move_assign(
21993      std::move(that),
21994      typename AllocTraits::propagate_on_container_move_assignment());
21995}
21996
21997iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
21998  return {};
21999}
22000
22001void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
22002
22003iterator insert(const_iterator hint, value_type&& value)
22004    ABSL_ATTRIBUTE_LIFETIME_BOUND {
22005  return {};
22006}
22007
22008friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
22009  return left.size() == right.size();
22010}
22011
22012static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
22013  return static_cast<slot_type*>(buffer);
22014}
22015
22016protected:
22017// Included-range recovery can attach this comment to the template prefix.
22018template <class K>
22019void AssertOnFind([[maybe_unused]] const K& key) {
22020  Check(key);
22021}
22022"#;
22023        let mut parser = tree_sitter::Parser::new();
22024        parser
22025            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22026            .unwrap();
22027        let tree = parser.parse(source, None).unwrap();
22028        assert!(
22029            tree.root_node().has_error(),
22030            "the fixture must exercise tree-sitter's errorful member shapes"
22031        );
22032        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
22033
22034        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
22035        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
22036        assert!(!cpp_reparsed_members_are_indexable(
22037            incomplete_tree.root_node(),
22038            incomplete_source
22039        ));
22040
22041        let outside_error_source = "int foo() stray_attribute {}\n";
22042        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
22043        assert!(outside_error_tree.root_node().has_error());
22044        assert!(!cpp_reparsed_members_are_indexable(
22045            outside_error_tree.root_node(),
22046            outside_error_source
22047        ));
22048
22049        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
22050        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
22051        assert!(!cpp_reparsed_members_are_indexable(
22052            variable_initializer_tree.root_node(),
22053            variable_initializer_source
22054        ));
22055    }
22056
22057    #[test]
22058    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
22059        let positive_source = r#"
22060std::pair<iterator, bool> insert(init_type&& value)
22061    ABSL_ATTRIBUTE_LIFETIME_BOUND
22062#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
22063  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
22064#endif
22065{
22066  return emplace(std::move(value));
22067}
22068"#;
22069        let mut parser = tree_sitter::Parser::new();
22070        parser
22071            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22072            .unwrap();
22073        let positive_tree = parser.parse(positive_source, None).unwrap();
22074        assert!(
22075            positive_tree.root_node().has_error(),
22076            "the fixture must exercise the split attribute/requires shape"
22077        );
22078        assert!(cpp_reparsed_members_are_indexable(
22079            positive_tree.root_node(),
22080            positive_source
22081        ));
22082
22083        let template_return_source = r#"
22084pair<int> insert(init_type&& value)
22085    ABSL_ATTRIBUTE_LIFETIME_BOUND
22086#if LANGUAGE_LEVEL >= 202002L
22087  requires(!Predicate<init_type>::value)
22088#endif
22089// Attributes and the function body may be separated by comments.
22090{
22091  return {};
22092}
22093"#;
22094        let template_return_tree = parser.parse(template_return_source, None).unwrap();
22095        assert!(
22096            cpp_reparsed_members_are_indexable(
22097                template_return_tree.root_node(),
22098                template_return_source
22099            ),
22100            "template-return attribute/requires tree: {}",
22101            template_return_tree.root_node().to_sexp()
22102        );
22103
22104        let no_body_source = r#"
22105std::pair<iterator, bool> insert(init_type&& value)
22106    ABSL_ATTRIBUTE_LIFETIME_BOUND
22107#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
22108  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
22109#endif
22110+ 0;
22111"#;
22112        let no_body_tree = parser.parse(no_body_source, None).unwrap();
22113        assert!(!cpp_reparsed_members_are_indexable(
22114            no_body_tree.root_node(),
22115            no_body_source
22116        ));
22117
22118        let extra_payload_source = r#"
22119pair<int> insert(init_type&& value)
22120    ABSL_ATTRIBUTE_LIFETIME_BOUND
22121#if LANGUAGE_LEVEL >= 202002L
22122  int unrelated;
22123  requires(Predicate<init_type>::value)
22124#endif
22125{
22126  return {};
22127}
22128"#;
22129        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
22130        assert!(!cpp_reparsed_members_are_indexable(
22131            extra_payload_tree.root_node(),
22132            extra_payload_source
22133        ));
22134
22135        let variable_initializer_source = r#"
22136int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
22137#if LANGUAGE_LEVEL >= 202002L
22138  requires(true)
22139#endif
22140{
22141  bad;
22142}
22143"#;
22144        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
22145        assert!(!cpp_reparsed_members_are_indexable(
22146            variable_initializer_tree.root_node(),
22147            variable_initializer_source
22148        ));
22149    }
22150
22151    #[test]
22152    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
22153        let source = r#"namespace absl {
22154ABSL_NAMESPACE_BEGIN namespace container_internal {
22155
22156class raw_hash_set : public Base {
22157 public:
22158  using value_type = int;
22159
22160  template <class U,
22161            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
22162  void insert(U value) { (void)value; }
22163
22164  struct InsertSlot {
22165    raw_hash_set& s;
22166  };
22167};
22168
22169}
22170ABSL_NAMESPACE_END
22171}"#;
22172        let mut parser = tree_sitter::Parser::new();
22173        parser
22174            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22175            .unwrap();
22176        let tree = parser.parse(source, None).unwrap();
22177        let root = tree.root_node();
22178        let outer_namespace = root
22179            .named_children(&mut root.walk())
22180            .find(|child| child.kind() == "namespace_definition")
22181            .expect("outer absl namespace");
22182        let declaration_list = outer_namespace
22183            .child_by_field_name("body")
22184            .expect("outer namespace body");
22185        let sentinel_function = declaration_list
22186            .named_children(&mut declaration_list.walk())
22187            .find(|child| child.kind() == "function_definition")
22188            .expect("malformed namespace sentinel function");
22189        let ancestry = ParentIndex::new(root);
22190        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
22191            .expect("structured nested namespace sentinel");
22192        let fragmented =
22193            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
22194                .expect("fragmented raw_hash_set class");
22195        assert_eq!(fragmented.class_node.kind(), "ERROR");
22196        assert_eq!(fragmented.name, "raw_hash_set");
22197        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
22198
22199        let outer_scope =
22200            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
22201        let mut outer_siblings = Vec::new();
22202        push_cpp_sentinel_sibling_classes(
22203            &mut outer_siblings,
22204            declaration_list,
22205            sentinel.function,
22206            &outer_scope,
22207            source,
22208            &ancestry,
22209        );
22210        let [outer_shadow] = outer_siblings.as_slice() else {
22211            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
22212        };
22213        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
22214        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
22215
22216        let field = "    raw_hash_set& s;";
22217        let start = source.find(field).expect("InsertSlot field") + 4;
22218        let node = root
22219            .descendant_for_byte_range(start, start + "raw_hash_set".len())
22220            .expect("raw_hash_set type node");
22221        let recovered = cpp_sentinel_recovered_classes(root, source);
22222        let [deep_class] = recovered.as_slice() else {
22223            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
22224        };
22225        assert_eq!(
22226            deep_class.namespace_scope_components,
22227            vec!["absl", "container_internal"]
22228        );
22229        assert_eq!(
22230            deep_class.scope_components,
22231            vec!["absl", "container_internal", "raw_hash_set"]
22232        );
22233        assert!(
22234            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
22235                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
22236        );
22237
22238        assert_eq!(
22239            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
22240            Some(vec![
22241                "absl".to_string(),
22242                "container_internal".to_string(),
22243                "raw_hash_set".to_string(),
22244                "InsertSlot".to_string(),
22245            ])
22246        );
22247
22248        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
22249        let parsed = parse_cpp_file(&file, source, &tree);
22250        let raw_hash_set = parsed
22251            .declarations()
22252            .iter()
22253            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
22254            .expect("recovered raw_hash_set class");
22255        assert_eq!(
22256            raw_hash_set.fq_name(),
22257            "absl::container_internal.raw_hash_set",
22258            "the recovered declaration must publish under the deeper sentinel namespace"
22259        );
22260        assert_eq!(
22261            parsed.raw_supertypes.get(raw_hash_set),
22262            Some(&vec!["Base".to_string()]),
22263            "the structured base clause on the fragmented ERROR prefix must survive publication"
22264        );
22265        assert!(
22266            parsed.materialization_records.iter().any(|record| matches!(
22267                record,
22268                MaterializationRecord::RecoveredDeclaration { recovery, unit }
22269                    if unit == raw_hash_set && *recovery == deep_class.class_range
22270            )),
22271            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
22272            parsed.materialization_records
22273        );
22274    }
22275
22276    /// Issue #2358: recording an aggregate definition must not walk the whole
22277    /// file.
22278    ///
22279    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
22280    /// class-like shape that has a body, so the removal step runs once per
22281    /// aggregate. It used to `retain` over `top_level_declarations` and over
22282    /// *every* child list in the file on each of those calls, comparing whole
22283    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
22284    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
22285    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
22286    /// tens of thousands of times over and the C forward differential never
22287    /// finished.
22288    ///
22289    /// A definition the file has not already declared removes nothing, so the
22290    /// honest cost is zero regardless of how many other aggregates surround it.
22291    /// Two sizes an order of magnitude apart pin that the count is not merely
22292    /// small but independent of the file.
22293    ///
22294    /// The declaration walk answers every ancestor question from a
22295    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
22296    /// the root for each one (#2361). Substituting the index is only safe
22297    /// because it answers the identical question, so pin that on the shapes
22298    /// this file's recovery paths care about: anonymous and named aggregates,
22299    /// nested namespaces, templates, macro-displaced declarations and the
22300    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
22301    /// too -- `Node::parent` walks the visible tree, not the named one.
22302    #[test]
22303    fn the_parent_index_answers_what_tree_sitter_answers() {
22304        const SHAPES: [&str; 5] = [
22305            "namespace outer { namespace inner { struct Tag { int field; }; } }",
22306            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
22307            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
22308            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
22309            "class API Broken : public First, public Second {\n  void member();\n",
22310        ];
22311        for source in SHAPES {
22312            let mut parser = tree_sitter::Parser::new();
22313            parser
22314                .set_language(&tree_sitter_cpp::LANGUAGE.into())
22315                .unwrap();
22316            let tree = parser.parse(source, None).unwrap();
22317            let root = tree.root_node();
22318            let ancestry = ParentIndex::new(root);
22319            let mut nodes = 0usize;
22320            let mut stack = vec![root];
22321            while let Some(node) = stack.pop() {
22322                nodes += 1;
22323                assert_eq!(
22324                    node.parent().map(|parent| parent.id()),
22325                    ancestry.parent(node).map(|parent| parent.id()),
22326                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
22327                );
22328                let mut cursor = node.walk();
22329                stack.extend(node.children(&mut cursor));
22330            }
22331            assert!(nodes > 1, "{source:?} produced no tree to compare");
22332        }
22333    }
22334
22335    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
22336    /// index for every ancestor edge. Asking tree-sitter directly makes each
22337    /// edge re-descend from the root, turning declaration extraction on a
22338    /// deeply nested generated header from quadratic output work into a cubic
22339    /// tree walk. Exact query counts pin the route without a machine-dependent
22340    /// wall-clock ceiling.
22341    #[test]
22342    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
22343        const DEPTH: usize = 64;
22344        let mut source = String::new();
22345        for level in 0..DEPTH {
22346            writeln!(source, "namespace n{level} {{").unwrap();
22347        }
22348        source.push_str("int deepest(int value);\n");
22349        for _ in 0..DEPTH {
22350            source.push_str("}\n");
22351        }
22352
22353        let mut parser = tree_sitter::Parser::new();
22354        parser
22355            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22356            .unwrap();
22357        let tree = parser.parse(&source, None).unwrap();
22358        let root = tree.root_node();
22359        let ancestry = ParentIndex::new(root);
22360        let mut function_declarator = None;
22361        walk_named_tree_preorder(root, true, |node| {
22362            if node.kind() == "function_declarator" {
22363                function_declarator = Some(node);
22364                WalkControl::Break
22365            } else {
22366                WalkControl::Continue
22367            }
22368        });
22369        let function_declarator = function_declarator.expect("deepest function declarator");
22370        let ancestor_count =
22371            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
22372
22373        ancestry.reset_parent_query_count_for_test();
22374        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
22375        assert_eq!(DEPTH, lexical_scope.len());
22376        assert_eq!(
22377            ancestor_count + 1,
22378            ancestry.parent_query_count_for_test(),
22379            "lexical-scope ancestry bypassed the parent index"
22380        );
22381
22382        ancestry.reset_parent_query_count_for_test();
22383        assert_eq!(
22384            Some(DispatchExtensibility::Closed),
22385            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
22386        );
22387        assert_eq!(
22388            ancestor_count,
22389            ancestry.parent_query_count_for_test(),
22390            "dispatch ancestry bypassed the parent index"
22391        );
22392
22393        ancestry.reset_parent_query_count_for_test();
22394        assert_eq!(
22395            CallableLinkage::External,
22396            cpp_callable_linkage(function_declarator, &source, &ancestry)
22397        );
22398        assert_eq!(
22399            ancestor_count + 1,
22400            ancestry.parent_query_count_for_test(),
22401            "linkage ancestry bypassed the parent index"
22402        );
22403
22404        ancestry.reset_parent_query_count_for_test();
22405        assert!(!cpp_callable_is_structural_constructor(
22406            function_declarator,
22407            &source,
22408            &ancestry
22409        ));
22410        assert_eq!(
22411            ancestor_count + 1,
22412            ancestry.parent_query_count_for_test(),
22413            "constructor ancestry bypassed the parent index"
22414        );
22415    }
22416
22417    /// #3507: the one row-level dispatch fact C++ can state is stated only
22418    /// where the language states it -- in the class body.
22419    ///
22420    /// A call's exactness is not readable from a definition row: an
22421    /// out-of-line definition repeats no `virtual`, no `final`, and no base
22422    /// clause, and C++ inherits virtual-ness from a base that declares the
22423    /// member. Each shape below therefore has to answer for itself, and only
22424    /// a class body with no base clause at all can close a member that writes
22425    /// nothing.
22426    #[test]
22427    fn callable_dispatch_extensibility_comes_from_the_class_body() {
22428        const SOURCE: &str = "\
22429class Plain { public: long send(long order); };
22430class Derived : public Base { public: long send(long order); };
22431class Sealed : public Base { public: long send(long order) final; };
22432class Hiding : public Base { public: long send(long order) { return order; } };
22433class Virtual { public: virtual long send(long order); };
22434namespace shop { long free_call(long order); }
22435long Plain::send(long order) { return order; }
22436";
22437        let mut parser = tree_sitter::Parser::new();
22438        parser
22439            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22440            .unwrap();
22441        let tree = parser.parse(SOURCE, None).unwrap();
22442        let root = tree.root_node();
22443        let ancestry = ParentIndex::new(root);
22444        let mut answers = Vec::new();
22445        walk_named_tree_preorder(root, true, |node| {
22446            if node.kind() != "function_declarator" {
22447                return WalkControl::Continue;
22448            }
22449            let name = cpp_function_declarator_name_node(node).expect("declarator name");
22450            answers.push((
22451                node_text(name, SOURCE).to_string(),
22452                cpp_callable_dispatch_extensibility(node, &ancestry),
22453            ));
22454            WalkControl::Continue
22455        });
22456        assert_eq!(
22457            answers,
22458            vec![
22459                ("send".to_string(), Some(DispatchExtensibility::Closed)),
22460                ("send".to_string(), Some(DispatchExtensibility::Open)),
22461                ("send".to_string(), Some(DispatchExtensibility::Closed)),
22462                ("send".to_string(), Some(DispatchExtensibility::Open)),
22463                ("send".to_string(), Some(DispatchExtensibility::Open)),
22464                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
22465                ("Plain::send".to_string(), None),
22466            ],
22467            "the class body states the fact; a qualified definition states none"
22468        );
22469    }
22470
22471    /// Forward declarations followed by definitions are compacted as one
22472    /// batch, without rescanning the shared namespace/top-level lists for each
22473    /// tag. Definitions are intentionally visited in reverse order so the
22474    /// assertion also pins eager remove-and-reappend ordering.
22475    #[test]
22476    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
22477        for aggregates in [64usize, 512] {
22478            let mut source =
22479                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
22480            for index in 0..aggregates {
22481                writeln!(source, "struct tag{index};").unwrap();
22482            }
22483            for index in (0..aggregates).rev() {
22484                writeln!(
22485                    source,
22486                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
22487                )
22488                .unwrap();
22489            }
22490            source.push_str("}\n");
22491
22492            start_code_unit_removal_scan_probe();
22493            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
22494            let scanned = finish_code_unit_removal_scan_probe();
22495
22496            let expected_names: Vec<String> = (0..aggregates)
22497                .rev()
22498                .map(|index| format!("tag{index}"))
22499                .collect();
22500            let top_level_names: Vec<String> = parsed
22501                .top_level_declarations
22502                .iter()
22503                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
22504                .map(|unit| unit.short_name().to_string())
22505                .collect();
22506            let namespace = parsed
22507                .declarations()
22508                .iter()
22509                .find(|unit| {
22510                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
22511                })
22512                .expect("generated namespace should be declared");
22513            let child_names: Vec<String> = parsed.children[namespace]
22514                .iter()
22515                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
22516                .map(|unit| unit.short_name().to_string())
22517                .collect();
22518            assert_eq!(
22519                aggregates,
22520                parsed
22521                    .declarations()
22522                    .iter()
22523                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
22524                    .count(),
22525                "every aggregate must still be declared at {aggregates} aggregates"
22526            );
22527            assert_eq!(expected_names, top_level_names);
22528            assert_eq!(expected_names, child_names);
22529            assert_eq!(
22530                0, scanned,
22531                "replacing {aggregates} forward declarations must compact their shared lists once"
22532            );
22533        }
22534    }
22535
22536    #[test]
22537    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
22538        const DISTINCT_PER_KIND: usize = 64;
22539        let mut source = String::new();
22540        for index in 0..DISTINCT_PER_KIND {
22541            writeln!(source, "typedef int Alias{index};").unwrap();
22542        }
22543        writeln!(source, "typedef long Alias0;").unwrap();
22544        for index in 0..DISTINCT_PER_KIND {
22545            writeln!(source, "#define MACRO_{index} {index}").unwrap();
22546        }
22547        writeln!(source, "#define MACRO_0 duplicate").unwrap();
22548        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
22549
22550        let mut parser = tree_sitter::Parser::new();
22551        parser
22552            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22553            .unwrap();
22554        let tree = parser.parse(&source, None).unwrap();
22555        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
22556
22557        start_declaration_identity_comparison_probe();
22558        let parsed = parse_cpp_file(&file, &source, &tree);
22559        let comparisons = finish_declaration_identity_comparison_probe();
22560
22561        assert_eq!(
22562            DISTINCT_PER_KIND + 1,
22563            parsed
22564                .declarations()
22565                .iter()
22566                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
22567                .count(),
22568            "every physical typedef alias declaration must be retained so \
22569             conditional branch guards stay available to the resolver"
22570        );
22571        assert_eq!(
22572            DISTINCT_PER_KIND + 1,
22573            parsed
22574                .declarations()
22575                .iter()
22576                .filter(|unit| {
22577                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
22578                })
22579                .count(),
22580            "distinct macro redefinitions must remain available to temporal lookup"
22581        );
22582        assert_eq!(
22583            2,
22584            parsed
22585                .declarations()
22586                .iter()
22587                .filter(|unit| {
22588                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
22589                })
22590                .count(),
22591            "function overloads must remain distinct"
22592        );
22593
22594        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
22595        assert!(
22596            comparisons <= dedup_inputs * 4,
22597            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
22598        );
22599    }
22600
22601    #[test]
22602    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
22603        let source = r#"namespace absl {
22604ABSL_NAMESPACE_BEGIN namespace container_internal {
22605template <typename T>
22606class broken {
22607 public:
22608  using value_type = T;
22609  T operator->() const { return &operator*(); }
22610  using alias = value_type;
22611};
22612}
22613}
22614"#;
22615        let mut parser = tree_sitter::Parser::new();
22616        parser
22617            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22618            .unwrap();
22619        let tree = parser.parse(source, None).unwrap();
22620        let broken = find_class_named(tree.root_node(), source, "broken")
22621            .expect("the positive fixture must expose the broken class node");
22622        assert!(
22623            broken.has_error(),
22624            "the positive fixture must retain an internal parser error"
22625        );
22626        assert!(
22627            cpp_complete_class_body_close(broken).is_some(),
22628            "the positive fixture must expose a real class body close"
22629        );
22630        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22631        assert!(
22632            recovered.iter().any(|class| {
22633                class.scope_components == ["absl", "container_internal", "broken"]
22634            }),
22635            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
22636        );
22637    }
22638
22639    #[test]
22640    fn sentinel_recovery_keeps_members_after_nested_body_close() {
22641        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
22642NLOHMANN_BASIC_JSON_TPL_DECLARATION
22643class basic_json {
22644 private:
22645  union storage {
22646    int value;
22647  } data;
22648 public:
22649  using late_alias = int;
22650  late_alias value() const;
22651};
22652NLOHMANN_JSON_NAMESPACE_END
22653"#;
22654        let mut parser = tree_sitter::Parser::new();
22655        parser
22656            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22657            .unwrap();
22658        let tree = parser.parse(source, None).unwrap();
22659        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22660        let basic_json = recovered
22661            .iter()
22662            .find(|class| {
22663                class
22664                    .scope_components
22665                    .last()
22666                    .is_some_and(|name| name == "basic_json")
22667            })
22668            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
22669        let late_alias = source
22670            .find("late_alias value")
22671            .expect("late alias reference");
22672        assert!(
22673            basic_json.class_range.start_byte < late_alias
22674                && late_alias < basic_json.class_range.end_byte,
22675            "the recovered class range must include members after a nested close: {basic_json:#?}"
22676        );
22677    }
22678
22679    #[test]
22680    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
22681        let source = r#"namespace absl {
22682ABSL_NAMESPACE_BEGIN namespace container_internal {
22683template <typename T>
22684class broken {
22685 public:
22686  using value_type = T;
22687  T operator->() const { return &operator*(); }
22688}
22689}
22690"#;
22691        let mut parser = tree_sitter::Parser::new();
22692        parser
22693            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22694            .unwrap();
22695        let tree = parser.parse(source, None).unwrap();
22696        let broken = find_class_named(tree.root_node(), source, "broken")
22697            .expect("the negative fixture must expose the malformed class node");
22698        assert!(
22699            broken.has_error(),
22700            "the negative fixture must retain a parser error"
22701        );
22702        assert!(
22703            cpp_complete_class_body_close(broken).is_none(),
22704            "the malformed class must not expose a real body close"
22705        );
22706        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22707        assert!(
22708            recovered
22709                .iter()
22710                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
22711            "an incomplete class must not borrow the namespace close: {recovered:#?}"
22712        );
22713    }
22714
22715    #[test]
22716    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
22717        let source = r#"namespace absl {
22718ABSL_NAMESPACE_BEGIN namespace container_internal {
22719template <typename T>
22720struct broken {
22721  using value_type = T;
22722};
22723}
22724
22725#ifdef OWNER_DEF
22726template <typename T>
22727typename broken<T>::value_type broken<T>::method() {
22728  value_type value{};
22729  return value;
22730}
22731#endif
22732
22733namespace sibling {
22734template <typename T>
22735typename broken<T>::value_type broken<T>::other() {
22736  value_type value{};
22737  return value;
22738}
22739}
22740
22741ABSL_NAMESPACE_END
22742}
22743"#;
22744        let mut parser = tree_sitter::Parser::new();
22745        parser
22746            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22747            .unwrap();
22748        let tree = parser.parse(source, None).unwrap();
22749        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22750        let broken = recovered
22751            .iter()
22752            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
22753            .expect("the sentinel class must be recovered");
22754        let method_start = source
22755            .find("typename broken<T>::value_type broken<T>::method()")
22756            .expect("guarded sibling owner");
22757        let method_end = source[method_start..]
22758            .find("\n}")
22759            .map(|offset| method_start + offset + 2)
22760            .expect("guarded sibling owner close");
22761        assert!(
22762            broken
22763                .owner_ranges
22764                .iter()
22765                .any(|owner| owner.range.start_byte <= method_start
22766                    && method_end <= owner.range.end_byte),
22767            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
22768        );
22769        let sibling_start = source
22770            .find("typename broken<T>::value_type broken<T>::other()")
22771            .expect("nested namespace sibling owner");
22772        assert!(
22773            broken
22774                .owner_ranges
22775                .iter()
22776                .all(|owner| owner.range.start_byte > sibling_start
22777                    || owner.range.end_byte <= sibling_start),
22778            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
22779        );
22780    }
22781
22782    #[test]
22783    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
22784        let source = r#"#ifdef OUTER
22785namespace absl {
22786ABSL_NAMESPACE_BEGIN namespace container_internal {
22787template <typename T>
22788struct broken {
22789  using value_type = T;
22790};
22791}
22792}
22793
22794#ifdef OWNER_DEF
22795template <typename T>
22796typename broken<T>::value_type broken<T>::method() {
22797  value_type value{};
22798  return value;
22799}
22800#endif
22801#endif
22802"#;
22803        let mut parser = tree_sitter::Parser::new();
22804        parser
22805            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22806            .unwrap();
22807        let tree = parser.parse(source, None).unwrap();
22808        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
22809        let broken = recovered
22810            .iter()
22811            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
22812            .expect("the sentinel class must be recovered");
22813        let method_start = source
22814            .find("typename broken<T>::value_type broken<T>::method()")
22815            .expect("outer sibling owner");
22816        assert!(
22817            broken
22818                .owner_ranges
22819                .iter()
22820                .all(|owner| owner.range.start_byte > method_start
22821                    || owner.range.end_byte <= method_start),
22822            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
22823        );
22824    }
22825
22826    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
22827    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
22828        let mut signatures = parsed
22829            .declarations()
22830            .iter()
22831            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
22832            .filter_map(|unit| unit.signature().map(str::to_string))
22833            .collect::<Vec<_>>();
22834        signatures.sort();
22835        signatures.dedup();
22836        signatures
22837    }
22838
22839    #[test]
22840    fn callable_parameter_types_come_from_the_ast_parameter_list() {
22841        let source = r#"
22842template <typename T, ENABLE_BYTES(T)>
22843Vec256<T> DupOdd(Vec256<T> value) { return value; }
22844
22845struct Visitor {
22846  void fail(this auto const& self) {}
22847};
22848"#;
22849        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
22850        let dup_odd = parsed
22851            .declarations()
22852            .iter()
22853            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
22854            .expect("DupOdd declaration");
22855        assert_eq!(
22856            dup_odd.signature(),
22857            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
22858        );
22859        assert_eq!(
22860            parsed
22861                .signature_metadata
22862                .get(dup_odd)
22863                .and_then(|metadata| metadata.first())
22864                .and_then(SignatureMetadata::callable_parameter_types),
22865            Some(["Vec256<T>".to_string()].as_slice())
22866        );
22867
22868        let fail = parsed
22869            .declarations()
22870            .iter()
22871            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
22872            .expect("explicit-object member");
22873        assert_eq!(fail.signature(), Some("(const this auto &)"));
22874        let metadata = parsed
22875            .signature_metadata
22876            .get(fail)
22877            .and_then(|metadata| metadata.first())
22878            .expect("explicit-object signature metadata");
22879        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
22880        assert!(
22881            metadata
22882                .callable_arity()
22883                .is_some_and(|arity| arity.accepts(0))
22884        );
22885    }
22886
22887    #[test]
22888    fn trailing_qualifiers_survive_parameter_list_whitespace() {
22889        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
22890        // declarator's structure, so an out-of-line definition that spells its
22891        // parameter list with different whitespace than the declaration must
22892        // still carry it.
22893        let source = r#"
22894struct Widget {
22895  bool multiline(int settings, int supprs) const;
22896  bool doublespace(int settings, int supprs) const;
22897  bool noexcept_multiline(int settings, int supprs) noexcept;
22898  bool ref_multiline(int settings, int supprs) &&;
22899};
22900bool
22901Widget::multiline (int settings,
22902                   int supprs) const
22903{ return settings + supprs > 0; }
22904bool Widget::doublespace(int settings,  int supprs) const { return true; }
22905bool Widget::noexcept_multiline(int settings,
22906                                int supprs) noexcept { return true; }
22907bool Widget::ref_multiline(int settings,
22908                           int supprs) && { return true; }
22909"#;
22910        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
22911        assert_eq!(
22912            vec!["(int, int) const".to_string()],
22913            identity_signatures(&parsed, "Widget.multiline")
22914        );
22915        assert_eq!(
22916            vec!["(int, int) const".to_string()],
22917            identity_signatures(&parsed, "Widget.doublespace")
22918        );
22919        assert_eq!(
22920            vec!["(int, int) noexcept".to_string()],
22921            identity_signatures(&parsed, "Widget.noexcept_multiline")
22922        );
22923        assert_eq!(
22924            vec!["(int, int) &&".to_string()],
22925            identity_signatures(&parsed, "Widget.ref_multiline")
22926        );
22927    }
22928
22929    #[test]
22930    fn macro_fragmented_plain_class_keeps_following_member_signature() {
22931        let source = r#"
22932struct CString {};
22933class CMessage {
22934public:
22935  CString GetParams(unsigned int index, unsigned int length = -1) const
22936      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
22937    return GetParamsColon(index, length);
22938  }
22939  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
22940};
22941CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
22942  return {};
22943}
22944"#;
22945        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
22946        assert_eq!(
22947            vec!["(unsigned int, unsigned int) const".to_string()],
22948            identity_signatures(&parsed, "CMessage.GetParamsColon")
22949        );
22950    }
22951
22952    #[test]
22953    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
22954        let source = r#"
22955#pragma once
22956#define DEMO_DEPRECATED(message)
22957namespace demo {
22958struct Base {
22959    static int aligned(int value) { return value; }
22960    int legacy(int value) const
22961        DEMO_DEPRECATED("use replacement()") { return value; }
22962    int replacement() const;
22963    void run(int value);
22964};
22965struct OtherBase {
22966    void run(int value);
22967    static int aligned(int value) { return value; }
22968};
22969struct Derived : Base {};
22970struct Override : Base {
22971    void run(int value);
22972    static int aligned(int value) { return value; }
22973};
22974struct RecoveredOverride : Base {
22975    int legacy(int value) const
22976        DEMO_DEPRECATED("use replacement()") { return value; }
22977    void run(int value);
22978};
22979struct Hidden : Base {
22980    void run(int first, int second);
22981    static int aligned(int first, int second) { return first + second; }
22982};
22983struct Ambiguous : Base, OtherBase {};
22984}
22985struct Global {};
22986"#;
22987        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
22988        let declarations = parsed.declarations();
22989        let fq_names = declarations
22990            .iter()
22991            .map(|unit| unit.fq_name())
22992            .collect::<std::collections::BTreeSet<_>>();
22993
22994        for expected in [
22995            "demo.Base",
22996            "demo.Base.aligned",
22997            "demo.Base.legacy",
22998            "demo.Base.replacement",
22999            "demo.Base.run",
23000            "demo.Derived",
23001            "demo.OtherBase",
23002            "demo.Override",
23003            "demo.RecoveredOverride",
23004            "demo.Hidden",
23005            "demo.Ambiguous",
23006            "Global",
23007        ] {
23008            assert!(
23009                fq_names.contains(expected),
23010                "missing {expected} from namespaced macro fragment: {declarations:#?}"
23011            );
23012        }
23013        assert!(
23014            !fq_names.contains("Derived"),
23015            "following class escaped its namespace: {declarations:#?}"
23016        );
23017        assert!(
23018            !fq_names.contains("demo.Global"),
23019            "global class crossed the recovered namespace boundary: {declarations:#?}"
23020        );
23021    }
23022
23023    #[test]
23024    fn trailing_qualifiers_still_separate_genuine_overloads() {
23025        // The qualifier must keep distinguishing the real C++ overload sets it
23026        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
23027        let source = r#"
23028struct Widget {
23029  int* slot(int index);
23030  const int* slot(int index) const;
23031  int log(int severity) &;
23032  int log(int severity) &&;
23033};
23034"#;
23035        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
23036        assert_eq!(
23037            vec!["(int)".to_string(), "(int) const".to_string()],
23038            identity_signatures(&parsed, "Widget.slot")
23039        );
23040        assert_eq!(
23041            vec!["(int) &".to_string(), "(int) &&".to_string()],
23042            identity_signatures(&parsed, "Widget.log")
23043        );
23044    }
23045
23046    #[test]
23047    fn virtual_specifier_is_not_part_of_the_identity_signature() {
23048        // `override` never appears on the out-of-line definition, and C++ does
23049        // not make it part of the signature, so it must not split the identity.
23050        let source = r#"
23051struct Base {
23052  virtual void run(int value) const;
23053};
23054struct Widget : Base {
23055  void run(int value) const override;
23056};
23057void Widget::run(int value) const {}
23058"#;
23059        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
23060        assert_eq!(
23061            vec!["(int) const".to_string()],
23062            identity_signatures(&parsed, "Widget.run")
23063        );
23064    }
23065
23066    #[test]
23067    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
23068        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
23069        // the function type, so a declaration that spells `const int` and a
23070        // definition that spells `int` are one entity.
23071        let source = r#"
23072struct Widget {
23073  bool value_params(const int settings, const int supprs);
23074  void pointee_const(const int* p);
23075  void pointer_const(int* const p);
23076  void both_const(const int* const p);
23077  void reference_const(const int& p);
23078  void array_const(const int values[4]);
23079};
23080bool Widget::value_params(int settings, int supprs) { return true; }
23081void Widget::pointer_const(int* p) {}
23082void Widget::both_const(const int* p) {}
23083"#;
23084        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
23085        assert_eq!(
23086            vec!["(int, int)".to_string()],
23087            identity_signatures(&parsed, "Widget.value_params")
23088        );
23089        assert_eq!(
23090            vec!["(int *)".to_string()],
23091            identity_signatures(&parsed, "Widget.pointer_const")
23092        );
23093        assert_eq!(
23094            vec!["(const int *)".to_string()],
23095            identity_signatures(&parsed, "Widget.both_const")
23096        );
23097        // The const that is not top-level still distinguishes the type.
23098        assert_eq!(
23099            vec!["(const int *)".to_string()],
23100            identity_signatures(&parsed, "Widget.pointee_const")
23101        );
23102        assert_eq!(
23103            vec!["(const int &)".to_string()],
23104            identity_signatures(&parsed, "Widget.reference_const")
23105        );
23106        assert_eq!(
23107            vec!["(const int [4])".to_string()],
23108            identity_signatures(&parsed, "Widget.array_const")
23109        );
23110    }
23111
23112    #[test]
23113    fn top_level_parameter_const_still_separates_pointee_overloads() {
23114        let source = r#"
23115struct Widget {
23116  void take(const int* p);
23117  void take(int* p);
23118};
23119"#;
23120        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
23121        assert_eq!(
23122            vec!["(const int *)".to_string(), "(int *)".to_string()],
23123            identity_signatures(&parsed, "Widget.take")
23124        );
23125    }
23126
23127    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
23128        let mut parser = tree_sitter::Parser::new();
23129        parser
23130            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23131            .unwrap();
23132        let tree = parser.parse(source, None).unwrap();
23133        let start = source.find(callable_name).expect("callable declaration");
23134        let declarator =
23135            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
23136        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
23137    }
23138
23139    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
23140        let mut shapes = comparable_shapes(source, callable_name);
23141        assert_eq!(1, shapes.len(), "{shapes:?}");
23142        match shapes.remove(0) {
23143            CppComparableSlot::Shape(shape) => shape,
23144            other => panic!("expected a comparable shape, got {other:?}"),
23145        }
23146    }
23147
23148    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
23149        let mut current = shape.root();
23150        loop {
23151            match shape.node(current) {
23152                CppComparableNode::Named { .. } => return shape.node(current),
23153                CppComparableNode::Pointer { inner, .. }
23154                | CppComparableNode::Reference { inner }
23155                | CppComparableNode::Array { inner } => current = *inner,
23156                CppComparableNode::Generic { base, .. } => current = *base,
23157            }
23158        }
23159    }
23160
23161    #[test]
23162    fn comparable_shape_keeps_pointee_const() {
23163        assert_ne!(
23164            sole_comparable_shape("void f(const char* p);", "f("),
23165            sole_comparable_shape("void f(char* p);", "f(")
23166        );
23167    }
23168
23169    #[test]
23170    fn comparable_shape_keeps_inner_pointer_const() {
23171        assert_ne!(
23172            sole_comparable_shape("void f(int** p);", "f("),
23173            sole_comparable_shape("void f(int* const* p);", "f(")
23174        );
23175    }
23176
23177    #[test]
23178    fn comparable_shape_drops_top_level_pointer_const() {
23179        assert_eq!(
23180            sole_comparable_shape("void f(int* const p);", "f("),
23181            sole_comparable_shape("void f(int* p);", "f(")
23182        );
23183    }
23184
23185    #[test]
23186    fn comparable_shape_drops_top_level_base_const() {
23187        assert_eq!(
23188            sole_comparable_shape("void f(const int p);", "f("),
23189            sole_comparable_shape("void f(int p);", "f(")
23190        );
23191    }
23192
23193    #[test]
23194    fn comparable_shape_decays_top_level_array_to_pointer() {
23195        assert_eq!(
23196            sole_comparable_shape("void f(int a[3]);", "f("),
23197            sole_comparable_shape("void f(int* a);", "f(")
23198        );
23199        assert_eq!(
23200            sole_comparable_shape("void f(int* a[3]);", "f("),
23201            sole_comparable_shape("void f(int** a);", "f(")
23202        );
23203    }
23204
23205    #[test]
23206    fn comparable_shape_keeps_array_behind_pointer() {
23207        assert_ne!(
23208            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
23209            sole_comparable_shape("struct S { void f(int** a); };", "f(")
23210        );
23211    }
23212
23213    #[test]
23214    fn comparable_shape_records_written_name_and_lexical_scope() {
23215        let declared =
23216            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
23217        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
23218        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
23219            panic!("named leaf");
23220        };
23221        assert_eq!(["Msg".to_string()].as_slice(), name.path());
23222        assert_eq!(
23223            ["ns".to_string(), "S".to_string()].as_slice(),
23224            name.lexical_scope()
23225        );
23226        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
23227            panic!("named leaf");
23228        };
23229        assert_eq!(
23230            ["ns".to_string(), "Msg".to_string()].as_slice(),
23231            name.path()
23232        );
23233        assert!(name.lexical_scope().is_empty());
23234        assert_ne!(declared, defined);
23235    }
23236
23237    #[test]
23238    fn comparable_shape_marks_sized_primitive_leaf() {
23239        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
23240        let CppComparableNode::Named {
23241            name, primitive, ..
23242        } = comparable_named_leaf(&shape)
23243        else {
23244            panic!("named leaf");
23245        };
23246        assert!(primitive);
23247        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
23248        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
23249    }
23250
23251    #[test]
23252    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
23253        assert_eq!(
23254            vec![CppComparableSlot::Unstructured],
23255            comparable_shapes("void f(void (*cb)(int));", "f(")
23256        );
23257    }
23258
23259    #[test]
23260    fn comparable_shape_reports_ellipsis_slot() {
23261        let shapes = comparable_shapes("void f(int a, ...);", "f(");
23262        assert_eq!(2, shapes.len(), "{shapes:?}");
23263        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
23264    }
23265
23266    #[test]
23267    fn comparable_shape_keeps_template_argument_const() {
23268        assert_ne!(
23269            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
23270            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
23271        );
23272    }
23273
23274    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
23275    /// file-scope tag that a later `struct inner *` at file scope may name.
23276    #[test]
23277    fn c_file_mints_aggregate_member_tag_at_file_scope() {
23278        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
23279        let parsed = parse_cpp_declarations(source, "x.c");
23280        let declarations = parsed.declarations();
23281
23282        assert!(
23283            declarations
23284                .iter()
23285                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
23286            "expected a file-scope inner tag, got {declarations:?}"
23287        );
23288        assert!(
23289            declarations
23290                .iter()
23291                .all(|unit| unit.fq_name() != "outer$inner"),
23292            "expected no nested identity, got {declarations:?}"
23293        );
23294        assert!(
23295            declarations
23296                .iter()
23297                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
23298        );
23299        // Members still belong to their own aggregate.
23300        assert!(
23301            declarations
23302                .iter()
23303                .any(|unit| unit.fq_name() == "inner.value")
23304        );
23305        assert!(
23306            declarations
23307                .iter()
23308                .any(|unit| unit.fq_name() == "outer.item")
23309        );
23310
23311        let outer = declarations
23312            .iter()
23313            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
23314            .expect("outer");
23315        assert!(
23316            parsed
23317                .children
23318                .get(outer)
23319                .into_iter()
23320                .flatten()
23321                .all(|child| child.fq_name() != "inner"),
23322            "the tag must not hang off the aggregate it is written inside: {:?}",
23323            parsed.children
23324        );
23325    }
23326
23327    /// A header carries no compilation language of its own, and a `.cpp`
23328    /// translation unit really does declare a nested class. Both keep exactly
23329    /// the C++ extraction they had before the C dialect existed.
23330    #[test]
23331    fn header_and_cpp_files_keep_nested_tag_identity() {
23332        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
23333        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
23334            let parsed = parse_cpp_declarations(source, name);
23335            let declarations = parsed.declarations();
23336            assert!(
23337                declarations
23338                    .iter()
23339                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
23340                "{name} must keep the nested identity, got {declarations:?}"
23341            );
23342            assert!(
23343                declarations.iter().all(|unit| unit.fq_name() != "inner"),
23344                "{name} must not mint a file-scope tag, got {declarations:?}"
23345            );
23346            assert!(
23347                declarations
23348                    .iter()
23349                    .any(|unit| unit.fq_name() == "outer$inner.value")
23350            );
23351        }
23352    }
23353
23354    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
23355    #[test]
23356    fn uppercase_c_extension_keeps_cpp_tag_scope() {
23357        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
23358        let parsed = parse_cpp_declarations(source, "x.C");
23359        assert!(
23360            parsed
23361                .declarations()
23362                .iter()
23363                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
23364        );
23365    }
23366
23367    /// There is no such thing as a partially nested tag in C: every level of a
23368    /// nested aggregate chain lands at the same enclosing scope.
23369    #[test]
23370    fn c_file_mints_every_nesting_level_at_file_scope() {
23371        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
23372        let parsed = parse_cpp_declarations(source, "z.c");
23373        let declarations = parsed.declarations();
23374
23375        for tag in ["a", "b", "c"] {
23376            assert!(
23377                declarations
23378                    .iter()
23379                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
23380                "expected a file-scope {tag}, got {declarations:?}"
23381            );
23382        }
23383        assert!(
23384            declarations
23385                .iter()
23386                .all(|unit| !unit.fq_name().contains('$')),
23387            "no level may keep a nested identity, got {declarations:?}"
23388        );
23389        // Each member still belongs to the aggregate that declares it.
23390        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
23391        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
23392        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
23393    }
23394
23395    /// An enum tag is a tag; its enumerators stay members of the enum, which is
23396    /// what makes them ordinary identifiers at the enum's own (file) scope.
23397    #[test]
23398    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
23399        let source = "struct outer { enum color { RED, GREEN } c; };\n";
23400        let parsed = parse_cpp_declarations(source, "e.c");
23401        let declarations = parsed.declarations();
23402
23403        let color = declarations
23404            .iter()
23405            .find(|unit| unit.is_class() && unit.fq_name() == "color")
23406            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
23407        assert!(
23408            declarations
23409                .iter()
23410                .all(|unit| unit.fq_name() != "outer$color")
23411        );
23412        for enumerator in ["color.RED", "color.GREEN"] {
23413            assert!(
23414                declarations.iter().any(|unit| unit.fq_name() == enumerator),
23415                "expected {enumerator}, got {declarations:?}"
23416            );
23417        }
23418        let children = parsed
23419            .children
23420            .get(color)
23421            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
23422        assert!(
23423            ["color.RED", "color.GREEN"]
23424                .iter()
23425                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
23426            "enumerators must hang off their enum: {children:?}"
23427        );
23428    }
23429
23430    #[test]
23431    fn c_file_mints_member_list_union_at_file_scope() {
23432        let source = "struct outer { union inner { int a; float b; } item; };\n";
23433        let parsed = parse_cpp_declarations(source, "u.c");
23434        let declarations = parsed.declarations();
23435        assert!(
23436            declarations
23437                .iter()
23438                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
23439            "expected a file-scope inner union, got {declarations:?}"
23440        );
23441        assert!(
23442            declarations
23443                .iter()
23444                .all(|unit| unit.fq_name() != "outer$inner")
23445        );
23446        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
23447        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
23448    }
23449
23450    /// A tag declared in a namespace member list is not a file-scope tag: the
23451    /// nearest enclosing non-aggregate scope is the namespace.
23452    #[test]
23453    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
23454        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
23455        let parsed = parse_cpp_declarations(source, "n.c");
23456        let declarations = parsed.declarations();
23457        let inner = declarations
23458            .iter()
23459            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
23460            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
23461        assert_eq!(inner.package_name(), "ns");
23462        assert!(
23463            declarations
23464                .iter()
23465                .all(|unit| unit.fq_name() != "ns.outer$inner")
23466        );
23467    }
23468
23469    /// Pins today's treatment of a tag declared inside a function body: the
23470    /// declaration walk does not descend into statement bodies, so no unit is
23471    /// minted for it in either dialect. C block scope is out of scope for the
23472    /// dialect change, and this test proves the change did not disturb it.
23473    #[test]
23474    fn function_local_tags_are_unchanged_in_both_dialects() {
23475        let source =
23476            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
23477        for name in ["y.c", "y.cpp"] {
23478            let parsed = parse_cpp_declarations(source, name);
23479            let declarations = parsed.declarations();
23480            assert!(
23481                declarations
23482                    .iter()
23483                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
23484                "{name}: {declarations:?}"
23485            );
23486            for tag in ["localtag", "deeper", "localtag$deeper"] {
23487                assert!(
23488                    declarations.iter().all(|unit| unit.fq_name() != tag),
23489                    "{name} must not mint {tag}, got {declarations:?}"
23490                );
23491            }
23492        }
23493    }
23494
23495    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
23496    /// re-scope: the typedef name is identical in both dialects.
23497    #[test]
23498    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
23499        let source = "typedef struct { int v; } T;\n";
23500        for name in ["t.c", "t.cpp"] {
23501            let parsed = parse_cpp_declarations(source, name);
23502            let declarations = parsed.declarations();
23503            assert!(
23504                declarations
23505                    .iter()
23506                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
23507                "{name}: {declarations:?}"
23508            );
23509        }
23510    }
23511
23512    /// The same principle for every anonymous aggregate the walk indexes, not
23513    /// just the typedef one: a promoted anonymous union in a member list, an
23514    /// anonymous struct given a receiver type by its declarator, a
23515    /// function-local aggregate, and one inside an inline member function
23516    /// body. The C++ reading used to mint none of these, so a pure C++
23517    /// workspace indexed no members at all (#3301).
23518    #[test]
23519    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
23520        let sources = [
23521            "struct outer { union { int a; float b; }; };\n",
23522            "struct outer { struct { int *ops; } sock; };\n",
23523            "void run(void) { struct { int v; } item; }\n",
23524            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
23525        ];
23526        for source in sources {
23527            let names = |name: &str| {
23528                parse_cpp_declarations(source, name)
23529                    .declarations()
23530                    .iter()
23531                    .map(|unit| unit.fq_name())
23532                    .collect::<std::collections::BTreeSet<_>>()
23533            };
23534            let c_names = names("a.c");
23535            let cpp_names = names("a.cpp");
23536            assert_eq!(
23537                c_names, cpp_names,
23538                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
23539            );
23540            assert!(
23541                c_names
23542                    .iter()
23543                    .any(|name| name.contains('.') && name != "outer"),
23544                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
23545            );
23546        }
23547    }
23548
23549    #[test]
23550    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
23551        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
23552        let parsed = parse_cpp_declarations(source, "socket.c");
23553        let declarations = parsed.declarations();
23554        assert_eq!(
23555            declarations
23556                .iter()
23557                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
23558                .count(),
23559            1,
23560            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
23561        );
23562        for expected in [
23563            "PAL_HANDLE",
23564            "PAL_HANDLE.sock",
23565            "PAL_HANDLE$sock",
23566            "PAL_HANDLE$sock.ops",
23567        ] {
23568            assert!(
23569                declarations.iter().any(|unit| unit.fq_name() == expected),
23570                "expected {expected}, got {declarations:?}"
23571            );
23572        }
23573    }
23574
23575    /// `class` is not C. Source that spells one in a `.c` file is not C code,
23576    /// so it keeps the C++ reading rather than acquiring a half-C identity.
23577    #[test]
23578    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
23579        let source = "class outer { class inner { int v; }; };\n";
23580        let c_parsed = parse_cpp_declarations(source, "k.c");
23581        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
23582        let c_declarations = c_parsed.declarations();
23583        let cpp_declarations = cpp_parsed.declarations();
23584        assert!(
23585            c_declarations
23586                .iter()
23587                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
23588            "{c_declarations:?}"
23589        );
23590        assert_eq!(
23591            c_declarations
23592                .iter()
23593                .map(|unit| unit.fq_name())
23594                .collect::<std::collections::BTreeSet<_>>(),
23595            cpp_declarations
23596                .iter()
23597                .map(|unit| unit.fq_name())
23598                .collect::<std::collections::BTreeSet<_>>()
23599        );
23600    }
23601
23602    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
23603    /// every (node, class-like name) pair a tree offers, and require the same
23604    /// answer from both.
23605    ///
23606    /// The release build has no `debug_assertions` agreement check, so this is
23607    /// what pins the two together there.  Both query orders are exercised:
23608    /// document order is what the walk does, and reverse order proves that a
23609    /// question about an earlier byte than one already answered is still
23610    /// filtered back to its own prefix rather than answered from the wider
23611    /// fold.
23612    ///
23613    /// Returns how many questions were answered with a namespace, so a fixture
23614    /// can assert it actually reached the path (#2754).
23615    fn namespace_forward_scan_agreement(source: &str) -> usize {
23616        let mut parser = tree_sitter::Parser::new();
23617        parser
23618            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23619            .unwrap();
23620        let tree = parser.parse(source, None).unwrap();
23621        let root = tree.root_node();
23622        let ancestry = ParentIndex::new(root);
23623
23624        let mut nodes = Vec::new();
23625        let mut names = std::collections::BTreeSet::new();
23626        let mut cursor = root.walk();
23627        let mut stack = vec![root];
23628        while let Some(node) = stack.pop() {
23629            if matches!(
23630                node.kind(),
23631                "class_specifier" | "struct_specifier" | "union_specifier"
23632            ) && let Some(name) = class_like_name(node, source, &ancestry)
23633            {
23634                names.insert(name);
23635            }
23636            nodes.push(node);
23637            stack.extend(node.named_children(&mut cursor));
23638        }
23639        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
23640        assert!(!names.is_empty(), "fixture declares no class-like name");
23641
23642        let mut answered = 0usize;
23643        for reversed in [false, true] {
23644            let mut scan = CppNamespaceForwardScan::default();
23645            let ordered: Vec<_> = if reversed {
23646                nodes.iter().rev().copied().collect()
23647            } else {
23648                nodes.clone()
23649            };
23650            answered = 0;
23651            for node in ordered {
23652                for name in &names {
23653                    scan.advance_to(root, node.start_byte(), source, &ancestry);
23654                    let carried = scan.unique_earlier_forward(name, node);
23655                    assert_eq!(
23656                        carried,
23657                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
23658                        "carried-forward scan and prefix scan disagree about {name} at \
23659                         {} node starting at byte {} (reversed order: {reversed})",
23660                        node.kind(),
23661                        node.start_byte()
23662                    );
23663                    answered += usize::from(carried.is_some());
23664                }
23665            }
23666        }
23667        answered
23668    }
23669
23670    /// A malformed namespace whose forward declarations are the only identity
23671    /// signal left for the class definitions tree-sitter pushed out to file
23672    /// scope.  Both recovered classes open the guard; only the first one is
23673    /// separated from the namespace by nothing but recovery trivia, so only the
23674    /// first one borrows.  The carried-forward scan has to reproduce both
23675    /// answers.
23676    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
23677namespace ns {
23678class Widget;
23679class Gadget;
23680int x = ;
23681}
23682class API Widget {
23683public:
23684    void first();
23685};
23686class API Gadget {
23687public:
23688    void second();
23689};
23690"#;
23691
23692    #[test]
23693    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
23694        assert!(
23695            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
23696            "the fixture must actually reach the namespace-borrow path"
23697        );
23698
23699        // Nothing here may be answered, and the two paths have to agree about
23700        // that too: a clean namespace is not an identity proof, two forwards of
23701        // one name are ambiguous rather than a guess, and a forward inside a
23702        // function body is not at namespace scope.
23703        for source in [
23704            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
23705            r#"#define API
23706namespace ns {
23707class Widget;
23708class Widget;
23709int x = ;
23710}
23711class API Widget {
23712public:
23713    void method();
23714};
23715"#,
23716            r#"#define API
23717namespace ns {
23718void host() {
23719    class Widget;
23720}
23721int x = ;
23722}
23723class API Widget {
23724public:
23725    void method();
23726};
23727"#,
23728        ] {
23729            assert_eq!(
23730                namespace_forward_scan_agreement(source),
23731                0,
23732                "no borrow is justified here: {source}"
23733            );
23734        }
23735    }
23736
23737    /// The fold is incremental, so a walk that asks about steadily later bytes
23738    /// must never re-fold a node an earlier question already folded, and must
23739    /// never skip one that lies between two questions.
23740    #[test]
23741    fn carried_forward_namespace_scan_folds_each_node_once() {
23742        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
23743        let mut parser = tree_sitter::Parser::new();
23744        parser
23745            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23746            .unwrap();
23747        let tree = parser.parse(source, None).unwrap();
23748        let root = tree.root_node();
23749        let ancestry = ParentIndex::new(root);
23750
23751        let mut incremental = CppNamespaceForwardScan::default();
23752        for cutoff in 0..=source.len() {
23753            incremental.advance_to(root, cutoff, source, &ancestry);
23754        }
23755        let mut whole = CppNamespaceForwardScan::default();
23756        whole.advance_to(root, source.len(), source, &ancestry);
23757
23758        let mut incremental_shape: Vec<_> = incremental
23759            .forwards
23760            .iter()
23761            .map(|(name, forwards)| {
23762                (
23763                    name.clone(),
23764                    forwards
23765                        .iter()
23766                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
23767                        .collect::<Vec<_>>(),
23768                )
23769            })
23770            .collect();
23771        let mut whole_shape: Vec<_> = whole
23772            .forwards
23773            .iter()
23774            .map(|(name, forwards)| {
23775                (
23776                    name.clone(),
23777                    forwards
23778                        .iter()
23779                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
23780                        .collect::<Vec<_>>(),
23781                )
23782            })
23783            .collect();
23784        incremental_shape.sort();
23785        whole_shape.sort();
23786        for (_, forwards) in &mut incremental_shape {
23787            forwards.sort();
23788        }
23789        for (_, forwards) in &mut whole_shape {
23790            forwards.sort();
23791        }
23792
23793        assert!(!whole_shape.is_empty(), "fixture folds no forward");
23794        assert_eq!(
23795            incremental_shape, whole_shape,
23796            "one byte at a time must fold exactly what one whole pass folds"
23797        );
23798    }
23799
23800    /// Drive the region reparse and the whitespace-padded reparse it replaced
23801    /// over the same region and require identical trees.
23802    ///
23803    /// The release build has no `debug_assertions` agreement check, so this is
23804    /// what pins the two together there. Each body is reparsed at its own
23805    /// offset and again after a long prefix, because the prefix is the whole
23806    /// difference between the two techniques: the padded parse lexes it as
23807    /// whitespace, the included-range parse never sees it, and the tree has to
23808    /// come out the same either way (#2788).
23809    fn fragmented_class_reparse_agreement(body: &str) {
23810        for prefix in [
23811            String::new(),
23812            "// leading comment\n".to_string(),
23813            // A body starts just after its class head's `{`, which is normally
23814            // mid-line: the padded parse then has spaces before the region on
23815            // the region's own line, and the included-range parse has nothing
23816            // at all before it.
23817            "class Widget : public Base { ".to_string(),
23818            "namespace filler {\n".to_string()
23819                + &"struct Filler { int member; };\n".repeat(200)
23820                + "}\n",
23821            "namespace filler {\n".to_string()
23822                + &"struct Filler { int member; };\n".repeat(200)
23823                + "}\nclass Widget : public Base { ",
23824        ] {
23825            let source = format!("{prefix}{body}");
23826            let start = prefix.len();
23827            let end = source.len();
23828            let region = cpp_reparse_fragmented_class_body(&source, start, end)
23829                .expect("the region reparse must produce a tree");
23830            let padded = cpp_reparse_padded_class_body(&source, start, end)
23831                .expect("the padded reparse must produce a tree");
23832            assert_eq!(
23833                cpp_tree_shape(&region),
23834                cpp_tree_shape(&padded),
23835                "region and padded reparse disagree at offset {start} of {end} bytes"
23836            );
23837            assert_eq!(
23838                region.root_node().start_byte(),
23839                start,
23840                "the reparsed region keeps its original offsets"
23841            );
23842        }
23843    }
23844
23845    #[test]
23846    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
23847        // A conditional immediately after an access label: the shape the padded
23848        // technique was kept for, because the directive and its macro name
23849        // become an ERROR plus the following declaration's apparent type.
23850        fragmented_class_reparse_agreement(
23851            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
23852        );
23853        fragmented_class_reparse_agreement(
23854            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
23855        );
23856        // The merged inline constructor and the nested fragmented bodies the
23857        // #938 recovery reads out of a reparse.
23858        fragmented_class_reparse_agreement(
23859            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
23860        );
23861        fragmented_class_reparse_agreement(
23862            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
23863        );
23864        // A body that is not member-shaped at all still has to reparse the same
23865        // way, because the admission gate reads the tree to reject it.
23866        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
23867    }
23868
23869    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
23870        let mut siblings = Vec::new();
23871        let mut anchor = node;
23872        while let Some(parent) = anchor.parent() {
23873            let at_translation_unit = parent.kind() == "translation_unit";
23874            let mut sibling = anchor.next_named_sibling();
23875            while let Some(current) = sibling {
23876                if at_translation_unit
23877                    && (current.kind() == "namespace_definition"
23878                        || (current.kind() == "function_definition"
23879                            && first_class_like_child(current).is_some()))
23880                {
23881                    return siblings;
23882                }
23883                siblings.push(current);
23884                if cpp_is_stray_close_brace(current, source) {
23885                    if let Some(semicolon) = current
23886                        .next_named_sibling()
23887                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
23888                    {
23889                        siblings.push(semicolon);
23890                    }
23891                    return siblings;
23892                }
23893                if current.start_byte() >= node.end_byte()
23894                    && matches!(current.kind(), "ERROR" | "labeled_statement")
23895                    && cpp_nested_stray_close_brace(current, source).is_some()
23896                {
23897                    return siblings;
23898                }
23899                sibling = current.next_named_sibling();
23900            }
23901            anchor = parent;
23902        }
23903        siblings
23904    }
23905
23906    #[test]
23907    fn following_named_siblings_cursor_matches_native_traversal() {
23908        // The damaged #2924 shape scatters members across container siblings,
23909        // and the anonymous punctuation forces the cursor to filter children
23910        // exactly as Node::next_named_sibling does.
23911        let source = r#"
23912namespace api {
23913class PROJECT_API(2, 0) Widget : public virtual Base {
23914  public:
23915    Widget() {}
23916    void first();
23917};
23918enum After { Value };
23919}
23920"#;
23921        let mut parser = tree_sitter::Parser::new();
23922        parser
23923            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23924            .unwrap();
23925        let tree = parser.parse(source, None).unwrap();
23926        let root = tree.root_node();
23927        assert!(root.has_error(), "fixture must remain damaged");
23928
23929        let ancestry = ParentIndex::new(root);
23930        let mut stack = vec![root];
23931        let mut checked = 0;
23932        let mut damaged = 0;
23933        let mut anonymous = 0;
23934        let mut nonempty_sequences = 0;
23935        let mut named_then_anonymous = 0;
23936        while let Some(node) = stack.pop() {
23937            let expected = native_following_named_siblings(node, source)
23938                .iter()
23939                .map(Node::id)
23940                .collect::<Vec<_>>();
23941            let actual = cpp_following_named_siblings(node, source, &ancestry)
23942                .iter()
23943                .map(Node::id)
23944                .collect::<Vec<_>>();
23945            assert_eq!(
23946                actual,
23947                expected,
23948                "cursor traversal diverged at byte {}",
23949                node.start_byte()
23950            );
23951
23952            let mut cursor = node.walk();
23953            let children = node.children(&mut cursor).collect::<Vec<_>>();
23954            stack.extend(children.iter().copied());
23955            checked += 1;
23956            damaged += usize::from(node.is_error() || node.is_missing());
23957            anonymous += usize::from(!node.is_named());
23958            nonempty_sequences += usize::from(!actual.is_empty());
23959            named_then_anonymous += usize::from(
23960                children
23961                    .windows(2)
23962                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
23963            );
23964        }
23965        assert!(checked > 0, "fixture must provide nodes to compare");
23966        assert!(damaged > 0, "fixture must cover damaged nodes");
23967        assert!(anonymous > 0, "fixture must cover anonymous nodes");
23968        assert!(
23969            nonempty_sequences > 0,
23970            "fixture must exercise following named siblings"
23971        );
23972        assert!(
23973            named_then_anonymous > 0,
23974            "fixture must exercise anonymous nodes following a named anchor"
23975        );
23976    }
23977
23978    /// A header shaped like the generated ones this walk is slow on: many
23979    /// enums, classes whose members share the enums' names, nested enums, an
23980    /// ownerless enumerator, and namespaced repeats of all of it.
23981    fn many_enums_and_mixed_declarations() -> String {
23982        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
23983        for index in 0..40 {
23984            let _ = write!(
23985                source,
23986                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
23987                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
23988                 class Color{index}Like {{ public: int member{index}; }};\n"
23989            );
23990        }
23991        source.push_str("namespace outer {\n");
23992        for index in 0..20 {
23993            let _ = write!(
23994                source,
23995                "enum Shade{index} {{ DARK{index} }};\n\
23996                 struct Shade{index}Holder {{ int field{index}; }};\n"
23997            );
23998        }
23999        source.push_str("}\n");
24000        source
24001    }
24002
24003    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
24004    /// every question a fixture's declarations can ask, and require the same
24005    /// answer from both.
24006    ///
24007    /// The release build has no `debug_assertions` agreement check, so this is
24008    /// what pins the two together there. The index is fed one declaration at a
24009    /// time and every question is re-asked after each one, which is what proves
24010    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
24011    /// test. Each of the fixture's own fields is also fed in restated as a
24012    /// declaration of another file: the scan ignores those because it asks
24013    /// about the asking unit's own source, and the index has to ignore them for
24014    /// the same reason.
24015    ///
24016    /// Returns how many questions the fixture answered `true`, so a caller can
24017    /// assert that it actually reached the path (#2786).
24018    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
24019        let parsed = parse_cpp_declarations(source, name);
24020        let file = ProjectFile::new(std::env::temp_dir(), name);
24021        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
24022
24023        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
24024        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
24025
24026        let foreign: Vec<CodeUnit> = declarations
24027            .iter()
24028            .filter(|unit| unit.kind() == CodeUnitType::Field)
24029            .map(|unit| {
24030                CodeUnit::new_fq(
24031                    elsewhere.clone(),
24032                    unit.kind(),
24033                    unit.package_name().to_string(),
24034                    unit.short_name().to_string(),
24035                    unit.fq().clone(),
24036                )
24037            })
24038            .collect();
24039
24040        // One owner chain deeper than any C++ short name reaches today
24041        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
24042        // the member). The scan asks `starts_with("owner.")`, so a field like
24043        // this answers for every owner in its chain, and the index has to
24044        // record every one of them rather than only the innermost.
24045        let mut packages: Vec<String> = declarations
24046            .iter()
24047            .map(|unit| unit.package_name().to_string())
24048            .collect();
24049        packages.push(String::new());
24050        packages.sort();
24051        packages.dedup();
24052        let deeper: Vec<CodeUnit> = packages
24053            .iter()
24054            .map(|package_name| {
24055                CodeUnit::new_fq(
24056                    file.clone(),
24057                    CodeUnitType::Field,
24058                    package_name.clone(),
24059                    "SynthOwner.middle.leaf".to_string(),
24060                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
24061                )
24062            })
24063            .collect();
24064
24065        // Every (package, owner) pair anything could ask about: each unit's own
24066        // short name, each dotted prefix of it, and the empty owner an
24067        // anonymous enum asks with (#2140).
24068        let mut questions: Vec<(String, String)> = Vec::new();
24069        for unit in declarations.iter().chain(deeper.iter()) {
24070            let package_name = unit.package_name().to_string();
24071            let short_name = unit.short_name();
24072            questions.push((package_name.clone(), short_name.to_string()));
24073            questions.push((package_name.clone(), String::new()));
24074            for (offset, _) in short_name.match_indices('.') {
24075                questions.push((package_name.clone(), short_name[..offset].to_string()));
24076            }
24077        }
24078        questions.sort();
24079        questions.dedup();
24080
24081        let mut index = CppFieldOwnerIndex::default();
24082        let mut recorded: Vec<&CodeUnit> = Vec::new();
24083        let mut answered = 0usize;
24084        for unit in foreign
24085            .iter()
24086            .chain(declarations.iter())
24087            .chain(deeper.iter())
24088        {
24089            index.record(unit, &file);
24090            recorded.push(unit);
24091            for (package_name, owner_short_name) in &questions {
24092                let carried = index.owns_fields(package_name, owner_short_name);
24093                assert_eq!(
24094                    carried,
24095                    cpp_declarations_hold_owned_fields(
24096                        recorded.iter().copied(),
24097                        &file,
24098                        package_name,
24099                        owner_short_name
24100                    ),
24101                    "the carried field index and the declaration scan disagree about \
24102                     {package_name:?}/{owner_short_name:?} after recording {}",
24103                    unit.fq_name()
24104                );
24105                answered += usize::from(carried);
24106            }
24107        }
24108
24109        // The whole-set build the first question performs must land on the same
24110        // index the incremental record built.
24111        let rebuilt = CppFieldOwnerIndex::of(
24112            foreign
24113                .iter()
24114                .chain(declarations.iter())
24115                .chain(deeper.iter()),
24116            &file,
24117        );
24118        for (package_name, owner_short_name) in &questions {
24119            assert_eq!(
24120                rebuilt.owns_fields(package_name, owner_short_name),
24121                index.owns_fields(package_name, owner_short_name),
24122                "a rebuilt index must answer what the incremental one answers for \
24123                 {package_name:?}/{owner_short_name:?}"
24124            );
24125        }
24126        answered
24127    }
24128
24129    /// The one thing the field index cannot absorb by addition: a deferred
24130    /// replacement of a declaration that owns children removes those children.
24131    ///
24132    /// The first enum builds the index, the struct records `Color.RED` into it,
24133    /// the body-less second `Color` replaces the first and takes `Color.RED`
24134    /// with it, and the last enum then asks about owner `Color`. An index that
24135    /// survived that removal answers `true` where the declarations say `false`,
24136    /// which is exactly what the in-walk agreement assertion catches (#2786).
24137    #[test]
24138    fn a_replacement_that_removes_children_drops_the_field_index() {
24139        let source =
24140            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
24141        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
24142        let mut names: Vec<_> = parsed
24143            .declarations()
24144            .iter()
24145            .map(|unit| unit.fq_name())
24146            .collect();
24147        names.sort();
24148        assert_eq!(
24149            names,
24150            vec![
24151                "Color".to_string(),
24152                "First".to_string(),
24153                "First.A".to_string()
24154            ],
24155            "the replaced Color owns no field any more"
24156        );
24157    }
24158
24159    /// A recovery that re-declares what the file already declared mints
24160    /// nothing.
24161    ///
24162    /// The reparse walk replaces the outer `Widget`, which removes its method,
24163    /// and then re-creates that method from the region. Creation alone would
24164    /// call the method recovered; it was there before the recovery opened, so
24165    /// the recovered set is empty and only the region's own reparse window is
24166    /// recorded (#2787).
24167    #[test]
24168    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
24169        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
24170                      BEGIN_NS\n\
24171                      namespace demo { struct Widget { void doWork(); }; }\n\
24172                      END_NS\n";
24173        let parsed = parse_cpp_declarations(source, "restored.cpp");
24174        let recovered: Vec<String> = parsed
24175            .materialization_records
24176            .iter()
24177            .filter_map(|record| match record {
24178                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
24179                _ => None,
24180            })
24181            .collect();
24182        assert!(
24183            recovered.is_empty(),
24184            "the region declares nothing the file did not already declare: {recovered:?}"
24185        );
24186        let mut names: Vec<String> = parsed
24187            .declarations()
24188            .iter()
24189            .map(|unit| unit.fq_name())
24190            .collect();
24191        names.sort();
24192        assert_eq!(
24193            names,
24194            vec![
24195                "demo".to_string(),
24196                "demo.Widget".to_string(),
24197                "demo.Widget.doWork".to_string(),
24198            ]
24199        );
24200    }
24201
24202    /// Four macro-sentinel recoveries in one file (#941). Each one must record
24203    /// exactly the declarations it minted -- not the ones an earlier recovery
24204    /// minted, and not the file's other declarations -- in start-byte order,
24205    /// and each record must carry its own reparse window (#2787).
24206    #[test]
24207    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
24208        let mut source = String::new();
24209        for index in 0..4 {
24210            let _ = write!(
24211                source,
24212                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
24213            );
24214        }
24215        source.push_str("void outside() {}\n");
24216        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
24217
24218        let recovered: Vec<(String, (usize, usize))> = parsed
24219            .materialization_records
24220            .iter()
24221            .filter_map(|record| match record {
24222                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
24223                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
24224                }
24225                _ => None,
24226            })
24227            .collect();
24228
24229        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
24230        for index in 0..4 {
24231            // The window the reparse covers: everything after the opening
24232            // sentinel token up to the newline before the closing one.
24233            let region = format!("namespace demo{index}");
24234            let region_start = source.find(&region).expect("each region is in the source");
24235            let start = source[..region_start]
24236                .rfind("BEGIN_NS")
24237                .expect("each region opens with a sentinel")
24238                + "BEGIN_NS".len();
24239            let end = start
24240                + source[start..]
24241                    .find("END_NS")
24242                    .expect("each region closes with a sentinel")
24243                - 1;
24244            let window = (start, end);
24245            for name in [
24246                format!("demo{index}"),
24247                format!("demo{index}.Widget{index}"),
24248                format!("demo{index}.Widget{index}.doWork{index}"),
24249            ] {
24250                expected.push((name, window));
24251            }
24252        }
24253        assert_eq!(
24254            recovered, expected,
24255            "each recovery records its own minted declarations, in order"
24256        );
24257        assert!(
24258            parsed
24259                .declarations()
24260                .iter()
24261                .any(|unit| unit.fq_name() == "outside"),
24262            "the declaration outside every region stays parsed and unrecovered"
24263        );
24264    }
24265
24266    #[test]
24267    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
24268        assert!(
24269            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
24270            "the fixture must actually own fields"
24271        );
24272
24273        // The shapes that make the two implementations diverge if the index
24274        // records the wrong keys: a nested enum's owner is its whole dotted
24275        // chain, a class named like an enum owns fields under that same name, a
24276        // `$` in a short name is an owner separator the dotted prefix rule must
24277        // not split on, and the same enum name in two namespaces is two owners.
24278        for (source, name) in [
24279            ("struct S { enum E { V }; };\n", "nested.hpp"),
24280            (
24281                "enum Color { RED };\nstruct Color { int RED; };\n",
24282                "class-like.c",
24283            ),
24284            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
24285            (
24286                "enum E { V };\nnamespace ns { enum E { V }; }\n",
24287                "repeated.hpp",
24288            ),
24289            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
24290        ] {
24291            field_owner_index_agreement(source, name);
24292        }
24293    }
24294
24295    /// `blocks` copies of one plain class-with-methods template. The class the
24296    /// question is about is always the first one, so the only thing that
24297    /// changes between two of these sources is how much unrelated tree
24298    /// surrounds it.
24299    fn repeated_class_blocks(blocks: usize) -> String {
24300        let mut source = String::from("namespace demo {\n");
24301        for index in 0..blocks {
24302            let _ = write!(
24303                source,
24304                "\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"
24305            );
24306        }
24307        source.push_str("\n}\n");
24308        source
24309    }
24310
24311    /// #1496: asking whether a recovered class shape claims one range must cost
24312    /// the path to that range, not a pass over the whole translation unit.
24313    ///
24314    /// The C++ inverse scan asks this once per candidate type reference, so a
24315    /// whole-tree walk makes one file's scan quadratic in its own size. Both
24316    /// sources here answer `None` -- these are plain classes that no recovery
24317    /// shape claims -- which is exactly the case that used to pay full price.
24318    #[test]
24319    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
24320        let mut answers = Vec::new();
24321        let mut visits = Vec::new();
24322        let mut node_counts = Vec::new();
24323        for blocks in [200usize, 400] {
24324            let source = repeated_class_blocks(blocks);
24325            let mut parser = tree_sitter::Parser::new();
24326            parser
24327                .set_language(&tree_sitter_cpp::LANGUAGE.into())
24328                .unwrap();
24329            let tree = parser.parse(&source, None).unwrap();
24330            let start_byte = source.find("class Widget0 ").expect("first class");
24331            let end_byte = start_byte
24332                + source[start_byte..]
24333                    .find("};")
24334                    .expect("first class terminator")
24335                + "};".len();
24336            let range = Range {
24337                start_byte,
24338                end_byte,
24339                start_line: 0,
24340                end_line: 0,
24341            };
24342            reset_recovered_class_body_node_visits_for_test();
24343            let recovered_export_classes =
24344                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
24345            answers.push(recovered_class_body_at(
24346                &recovered_export_classes,
24347                tree.root_node(),
24348                &source,
24349                "Widget0",
24350                &range,
24351            ));
24352            visits.push(recovered_class_body_node_visits_for_test());
24353            let mut nodes = 0usize;
24354            let mut stack = vec![tree.root_node()];
24355            while let Some(node) = stack.pop() {
24356                nodes += 1;
24357                let mut cursor = node.walk();
24358                stack.extend(node.named_children(&mut cursor));
24359            }
24360            node_counts.push(nodes);
24361        }
24362
24363        assert_eq!(
24364            answers,
24365            vec![None, None],
24366            "no recovered shape claims a plain class"
24367        );
24368        assert_eq!(
24369            visits[0], visits[1],
24370            "the walk must follow the range's own path, so doubling the unrelated \
24371             classes must not change the node count: {visits:?} over trees of \
24372             {node_counts:?} nodes"
24373        );
24374        assert!(
24375            visits[1] * 20 < node_counts[1],
24376            "the walk must stay far below one pass over the tree: {visits:?} over \
24377             trees of {node_counts:?} nodes"
24378        );
24379    }
24380
24381    #[test]
24382    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
24383        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
24384        let mut parser = tree_sitter::Parser::new();
24385        parser
24386            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24387            .expect("C++ grammar");
24388        let tree = parser.parse(source, None).expect("fixture tree");
24389        let mut stack = vec![tree.root_node()];
24390        let mut recovered = None;
24391        while let Some(node) = stack.pop() {
24392            if node.kind() == "field_declaration"
24393                && let Some(field) = recovered_function_like_field_declarator(node, source)
24394            {
24395                recovered = Some((node, field.name));
24396                break;
24397            }
24398            let mut cursor = node.walk();
24399            stack.extend(node.named_children(&mut cursor));
24400        }
24401        let (declaration, name) = recovered.unwrap_or_else(|| {
24402            panic!(
24403                "pointer-wrapped macro field was not recovered: {}",
24404                tree.root_node().to_sexp()
24405            )
24406        });
24407        assert_eq!(node_text(name, source), "handshake");
24408        let recovered =
24409            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
24410        assert_eq!(recovered.pointer_depth(), 1);
24411        assert_eq!(
24412            render_cpp_field_signature(declaration, name, source),
24413            "struct handshake * handshake;"
24414        );
24415    }
24416
24417    #[test]
24418    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
24419        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
24420        let mut parser = tree_sitter::Parser::new();
24421        parser
24422            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24423            .expect("C++ grammar");
24424        let tree = parser.parse(source, None).expect("fixture tree");
24425        let mut stack = vec![tree.root_node()];
24426        let mut recovered = None;
24427        while let Some(node) = stack.pop() {
24428            if let Some(field) = recovered_pyobject_head_field(node, source) {
24429                recovered = Some((node, field));
24430                break;
24431            }
24432            let mut cursor = node.walk();
24433            stack.extend(node.named_children(&mut cursor));
24434        }
24435        let (declaration, recovered) = recovered.unwrap_or_else(|| {
24436            panic!(
24437                "pointer field after PyObject_HEAD was not recovered: {}",
24438                tree.root_node().to_sexp()
24439            )
24440        });
24441        assert_eq!(node_text(recovered.name, source), "image");
24442        assert_eq!(recovered.pointer_depth(), 1);
24443        assert_eq!(
24444            render_cpp_field_signature(declaration, recovered.declarator, source),
24445            "ImagingObject *image;"
24446        );
24447        assert_eq!(
24448            crate::source_facts::capture_field_type(declaration, "image", source),
24449            (
24450                Some(
24451                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
24452                        type_text: "ImagingObject".to_owned(),
24453                        indirection: 1,
24454                        binds_indirectly: true,
24455                        template_arguments: None,
24456                    }
24457                ),
24458                false,
24459            ),
24460            "canonical field facts must use the recovered type, not the macro token"
24461        );
24462    }
24463
24464    /// The C source shapes whose callable modifiers the walk reads, and what
24465    /// each one states. C has no member functions at all, so the only fact its
24466    /// declarations carry is Unknown; linkage is separate. The `static`
24467    /// a C function writes is internal linkage, never a receiver.
24468    #[test]
24469    fn callable_metadata_records_c_receiver_contracts_structurally() {
24470        const SOURCE: &str = concat!(
24471            "static long accumulate(const long *lines, int count) { return count; }\n",
24472            "\n",
24473            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
24474        );
24475        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
24476
24477        assert_eq!(
24478            recorded_callable_modifiers(&parsed),
24479            vec![
24480                "accumulate decl_only=false static=false constructor=false \
24481                 visibility=Unknown"
24482                    .to_string(),
24483                "total decl_only=false static=false constructor=false visibility=Unknown"
24484                    .to_string(),
24485            ],
24486        );
24487    }
24488
24489    /// The C++ source shapes whose callable modifiers the walk reads.
24490    ///
24491    /// C++ states the receiver fact in the declaration's position: a class-body
24492    /// member binds the object it is selected on unless it writes `static`, and
24493    /// its access is the one the body's specifier ladder is under. The
24494    /// out-of-line definitions state neither -- C++ forbids them from repeating
24495    /// `static` and they write no access specifier -- which is why
24496    /// `Client::count` is recorded static only on the declaration-only row, the
24497    /// one whose `static` is actually written. An in-class prototype and the
24498    /// out-of-line definition completing it in this translation unit are two
24499    /// rows of one code unit (#3508), so their rows share the unit's name and
24500    /// `decl_only` is what separates them.
24501    #[test]
24502    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
24503        const SOURCE: &str = concat!(
24504            "namespace shop {\n",
24505            "\n",
24506            "class Client {\n",
24507            "public:\n",
24508            "    Client();\n",
24509            "    void send(int order);\n",
24510            "    static int count();\n",
24511            "protected:\n",
24512            "    virtual int audit(int n) const { return n; }\n",
24513            "private:\n",
24514            "    int tally(int n) { return n; }\n",
24515            "};\n",
24516            "\n",
24517            "struct Ledger {\n",
24518            "    void record(int n) { (void)n; }\n",
24519            "};\n",
24520            "\n",
24521            "Client::Client() {}\n",
24522            "void Client::send(int order) { (void)order; }\n",
24523            "int Client::count() { return 0; }\n",
24524            "\n",
24525            "int total(int order) { return order; }\n",
24526            "\n",
24527            "}\n",
24528        );
24529        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
24530
24531        assert_eq!(
24532            recorded_callable_modifiers(&parsed),
24533            vec![
24534                "shop.Client.Client decl_only=false static=false constructor=true \
24535                 visibility=Unknown"
24536                    .to_string(),
24537                "shop.Client.Client decl_only=true static=false constructor=true \
24538                 visibility=Public"
24539                    .to_string(),
24540                "shop.Client.audit decl_only=false static=false constructor=false \
24541                 visibility=Protected"
24542                    .to_string(),
24543                "shop.Client.count decl_only=false static=false constructor=false \
24544                 visibility=Unknown"
24545                    .to_string(),
24546                "shop.Client.count decl_only=true static=true constructor=false \
24547                 visibility=Public"
24548                    .to_string(),
24549                "shop.Client.send decl_only=false static=false constructor=false \
24550                 visibility=Unknown"
24551                    .to_string(),
24552                "shop.Client.send decl_only=true static=false constructor=false \
24553                 visibility=Public"
24554                    .to_string(),
24555                "shop.Client.tally decl_only=false static=false constructor=false \
24556                 visibility=Private"
24557                    .to_string(),
24558                "shop.Ledger.record decl_only=false static=false constructor=false \
24559                 visibility=Public"
24560                    .to_string(),
24561                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
24562                    .to_string(),
24563            ],
24564        );
24565    }
24566
24567    /// One readable row per recorded callable signature entry.
24568    ///
24569    /// An in-class prototype and the out-of-line definition completing it are
24570    /// two entries -- of one code unit when one translation unit holds both
24571    /// (#3508) -- and the two state different modifier facts, so the row says
24572    /// which declaration role it read.
24573    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
24574        let mut rows = parsed
24575            .signature_metadata
24576            .iter()
24577            .flat_map(|(unit, entries)| {
24578                entries.iter().map(move |metadata| {
24579                    assert!(
24580                        metadata.callable_modifiers_recorded(),
24581                        "{} must record that the walk read its declaration shape",
24582                        unit.fq_name()
24583                    );
24584                    format!(
24585                        "{} decl_only={} static={} constructor={} visibility={:?}",
24586                        unit.fq_name(),
24587                        metadata.is_declaration_only(),
24588                        metadata.callable_is_static(),
24589                        metadata.callable_is_constructor(),
24590                        metadata
24591                            .callable_declared_visibility()
24592                            .expect("a recorded modifier set states a visibility"),
24593                    )
24594                })
24595            })
24596            .collect::<Vec<_>>();
24597        rows.sort();
24598        rows
24599    }
24600}