Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
11use brokk_bifrost_core::analyzer::common::{
12    parse_source_ranges_with_cancellation, parse_source_region,
13};
14pub use brokk_bifrost_core::analyzer::cpp_facts::{
15    CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
16    CppComparableSlot,
17};
18use brokk_bifrost_core::analyzer::fq_name::{
19    FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
20};
21use brokk_bifrost_core::analyzer::model::LanguageDialect;
22use brokk_bifrost_core::analyzer::model::{
23    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
24    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
25    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
26    ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
27    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
28};
29use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
30use brokk_bifrost_core::analyzer::source_facts::{
31    PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
32    SourceOccurrenceProvenance,
33};
34use brokk_bifrost_core::analyzer::statement_facts::CppStatementFactRows;
35use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
36use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
37use brokk_bifrost_core::analyzer::structural::materialization::{
38    GenerationKind, MaterializationRecord,
39};
40use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
41use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
42use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
43use brokk_bifrost_core::analyzer::tree_walk::{
44    NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
45    named_children_iter, push_children_reversed, push_named_children_reversed,
46    walk_named_tree_preorder,
47};
48use brokk_bifrost_core::analyzer::usages::common::same_node;
49use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
50use brokk_bifrost_core::hash::{HashMap, HashSet};
51use regex::Regex;
52use std::cell::RefCell;
53use tree_sitter::{Node, Tree};
54
55/// Strip a tag keyword from the type spelling returned by C++ tooling.
56pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
57    let value = value.trim_start_matches("const ");
58    value
59        .strip_prefix("struct ")
60        .or_else(|| value.strip_prefix("class "))
61        .or_else(|| value.strip_prefix("enum "))
62        .unwrap_or(value)
63        .trim()
64}
65
66/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
67/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
68/// `::` head is exactly the mixed-separator store issue #1163 is about; the
69/// structured form records each namespace component, and the equivalence check
70/// renders it natively (with `::` between adjacent Package segments) so it
71/// round-trips to the legacy string. Splitting the already-joined string here is
72/// the M1 bridge — the legacy strings stay authoritative until M3.
73fn cpp_push_package(fq: &mut FqName, package_name: &str) {
74    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
75        fq.push(segment_interner().intern(component, SegmentKind::Package));
76    }
77}
78
79/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
80const CPP_PACKAGE_SEPARATOR: &str = "::";
81
82/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
83/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
84/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
85/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
86/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
87/// cpp-specific native rendering rule is needed for this chain.
88fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
89    let mut first = true;
90    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
91    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
92    // entry point, not re-inference of an already-structured name.
93    for component in chain.split('$').filter(|c| !c.is_empty()) {
94        let kind = if first {
95            SegmentKind::Type
96        } else {
97            SegmentKind::Nested
98        };
99        fq.push(segment_interner().intern(component, kind));
100        first = false;
101    }
102}
103
104/// Structured name for a C++ namespace module: every `::`-separated component is
105/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
106/// `short_name` with an empty `package_name`).
107fn cpp_namespace_fq(full_name: &str) -> FqName {
108    let mut fq = FqName::new();
109    cpp_push_package(&mut fq, full_name);
110    fq
111}
112
113/// The per-level namespace components a `namespace_definition`'s `name` field
114/// declares.
115///
116/// A C++17 nested definition (`namespace a::b::c`) parses as a
117/// `nested_namespace_specifier` whose named children are the per-level
118/// `namespace_identifier`s plus, for three or more levels, a further
119/// `nested_namespace_specifier`; the `::` separators, the optional per-level
120/// `inline`, and the leading global `::` are all anonymous tokens the walk
121/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
122/// segment path as the expanded `namespace a { namespace b { } }` form.
123///
124/// A shape outside that grammar is the deliberately ill-formed source the
125/// diagnostic corpora carry. Those keep their historical single-component
126/// reading of the raw name text, which the caller still joins to the lexical
127/// namespace exactly as before.
128fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
129    let mut components = Vec::new();
130    let mut stack = vec![node];
131    while let Some(current) = stack.pop() {
132        match current.kind() {
133            "namespace_identifier" | "identifier" => {
134                components.push(normalize_cpp_whitespace(node_text(current, source)));
135            }
136            "nested_namespace_specifier" => {
137                for index in (0..current.named_child_count()).rev() {
138                    stack.push(
139                        current
140                            .named_child(index)
141                            .expect("index below the node's own named child count"),
142                    );
143                }
144            }
145            _ => return cpp_raw_namespace_name_components(node, source),
146        }
147    }
148    if components.iter().any(String::is_empty) {
149        return cpp_raw_namespace_name_components(node, source);
150    }
151    components
152}
153
154/// The historical reading of a namespace name node: its whole source text as
155/// one component, with a leading global `::` marker dropped so the caller's
156/// global-scope handling stays the AST boundary rather than a text prefix.
157///
158/// This is the recovery path for source outside the C++ grammar, so the text it
159/// returns can carry a separator that names nothing. react-native-windows
160/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
161/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
162/// trailing separator with no tail. The caller stores this component in
163/// `short_name` and derives the fq by splitting it back apart, so an empty tail
164/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
165/// here, at the one place the malformed text enters, rather than leaving each
166/// consumer to guard (#2353, a variant of #1878).
167fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
168    let start = node
169        .child(0)
170        .filter(|child| !child.is_named() && child.kind() == "::")
171        .map_or(node.start_byte(), |marker| marker.end_byte());
172    let text = normalize_cpp_whitespace(
173        source
174            .get(start..node.end_byte())
175            .expect("namespace name node covers one source range"),
176    );
177    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
178    if text.is_empty() {
179        return Vec::new();
180    }
181    vec![text]
182}
183
184/// The namespace definition ancestors of `node`, ordered from outermost to innermost.
185pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
186    let mut namespaces = Vec::new();
187    let mut current = node.parent();
188    while let Some(parent) = current {
189        if parent.kind() == "namespace_definition" {
190            namespaces.push(parent);
191        }
192        current = parent.parent();
193    }
194    namespaces.reverse();
195    namespaces
196}
197
198/// Return the named namespace path that structurally encloses `node`.
199///
200/// This intentionally follows namespace AST ancestors rather than inspecting
201/// source text. Anonymous namespaces are not representable in the legacy C++
202/// package field, so a path containing one fails closed.
203fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
204    let mut components = Vec::new();
205    for current in enclosing_cpp_namespace_nodes(node) {
206        let name_node = current.child_by_field_name("name")?;
207        let name = normalize_cpp_whitespace(node_text(name_node, source));
208        if name.is_empty() {
209            return None;
210        }
211        components.push(name);
212    }
213    if components.is_empty() {
214        return None;
215    }
216    // Same shared empty-component decision as `cpp_push_package`, which splits
217    // this string back apart: an enclosing namespace whose own name node is
218    // malformed contributes a component carrying its own separator (#2353).
219    Some(
220        normalize_joined(
221            &components.join(CPP_PACKAGE_SEPARATOR),
222            CPP_PACKAGE_SEPARATOR,
223        )
224        .into_owned(),
225    )
226}
227
228/// Nested-class `$` join for short names. An anonymous parent class (empty
229/// short_name) contributes no segment: the FqName bridge drops empty
230/// components, so a bare `parent$child` join would desync `short_name` from
231/// the fq and trip the package/short boundary assert in
232/// `CodeUnit::with_signature_and_fq` (#2140).
233fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
234    cpp_join_short_name(parent_short, name, '$')
235}
236
237/// Member `.` join for short names; same anonymous-parent guard as
238/// [`cpp_join_nested_short`] (#2140).
239fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
240    cpp_join_short_name(parent_short, name, '.')
241}
242
243fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
244    if parent_short.is_empty() {
245        name.to_string()
246    } else {
247        format!("{parent_short}{separator}{name}")
248    }
249}
250
251/// Structural fq for a leaf declaration: the parent unit's fq plus this
252/// declaration's own name as one segment (or the package segments plus the
253/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
254/// chain, so a literal `$` inside a source identifier (Cython template
255/// substitution points, gcc `$`-identifiers) survives instead of corrupting
256/// the chain and tripping the package/short boundary assert (#2140).
257fn cpp_leaf_fq(
258    package_name: &str,
259    parent: Option<&CodeUnit>,
260    name: &str,
261    kind_if_nested: SegmentKind,
262    kind_if_top: SegmentKind,
263) -> FqName {
264    if let Some(parent) = parent {
265        parent
266            .fq()
267            .clone()
268            .with_pushed(segment_interner().intern(name, kind_if_nested))
269    } else {
270        let mut fq = FqName::new();
271        cpp_push_package(&mut fq, package_name);
272        fq.push(segment_interner().intern(name, kind_if_top));
273        fq
274    }
275}
276
277/// Structured name for a member unit (function, field, enumerator). The
278/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
279/// the member has an owner, by `.member`; free functions and globals have no
280/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
281/// C++ member names never contain a literal `.`, so the single `.` (if any)
282/// separates the owner chain from the member.
283pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
284    let mut fq = FqName::new();
285    cpp_push_package(&mut fq, package_name);
286    match short_name.rsplit_once('.') {
287        Some((owner_chain, member)) => {
288            cpp_push_type_chain(&mut fq, owner_chain);
289            fq.push(segment_interner().intern(member, SegmentKind::Member));
290        }
291        None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
292    }
293    fq
294}
295
296/// Structured name for a rendered C++ qualified name supplied at the input
297/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
298///
299/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
300/// it hands back one component for a nested-class chain (`Outer$Inner`). An
301/// exact-name lookup then asks the store for owner `ns` with identifier
302/// `Outer$Inner`, while the declaration persists as a
303/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
304/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
305/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
306/// boundary identical to the persisted one. A `$` that is *not* a nesting join
307/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
308/// and stays reachable through the identifier fallback, which seeks the whole
309/// component text as one spelling.
310pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
311    let mut fq = FqName::new();
312    for component in parse_symbol_path(Language::Cpp, rendered) {
313        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
314        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
315        // construction boundary `cpp_push_type_chain` owns for extraction.
316        let mut first = true;
317        for part in component.split('$').filter(|part| !part.is_empty()) {
318            let kind = if first {
319                // A client-typed head claims no kind, exactly as
320                // `parse_symbol_path_fq` records it.
321                SegmentKind::Unknown
322            } else {
323                SegmentKind::Nested
324            };
325            fq.push(segment_interner().intern(part, kind));
326            first = false;
327        }
328    }
329    fq
330}
331
332/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
333///
334/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
335/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
336/// `Top.Only`, and the exact-name retain drops the persisted nested identity
337/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
338/// placements extraction stores, so each exact query's parent and terminal
339/// match a persisted row and the structural retain keeps it.
340///
341/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
342/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
343/// is one of those: the `::` there is the package join the canonical name
344/// already spells.
345pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
346    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
347        return Vec::new();
348    }
349    let texts = parse_symbol_path(Language::Cpp, rendered);
350    if texts.len() < 2 {
351        return Vec::new();
352    }
353    cpp_qualified_kind_interpretations(&texts)
354}
355
356/// Every namespace / type / member boundary a `::`-joined source spelling can
357/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
358/// segments.
359fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
360    let len = texts.len();
361    let mut names = Vec::new();
362    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
363    for packages in 0..len {
364        let rest = len - packages;
365        let mut type_chain = Vec::with_capacity(len);
366        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
367        type_chain.push(SegmentKind::Type);
368        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
369        names.push(cpp_fq_with_kinds(texts, type_chain));
370        if rest >= 2 {
371            let mut member = Vec::with_capacity(len);
372            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
373            member.push(SegmentKind::Type);
374            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
375            member.push(SegmentKind::Member);
376            names.push(cpp_fq_with_kinds(texts, member));
377        } else if packages >= 1 {
378            let mut function = vec![SegmentKind::Package; packages];
379            function.push(SegmentKind::Member);
380            names.push(cpp_fq_with_kinds(texts, function));
381        }
382    }
383    names
384}
385
386fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
387    assert_eq!(
388        texts.len(),
389        kinds.len(),
390        "a qualified interpretation names every segment"
391    );
392    let mut fq = FqName::new();
393    for (text, kind) in texts.iter().zip(kinds) {
394        fq.push(segment_interner().intern(text, kind));
395    }
396    fq
397}
398
399#[derive(Clone)]
400pub struct ScopeInfo {
401    package_name: String,
402    module: Option<CodeUnit>,
403    class_unit: Option<CodeUnit>,
404    template_signature: Option<String>,
405    template_metadata: Option<CppTemplateMetadata>,
406    declarations_are_fields: bool,
407    recovered_specialization_member_scope: bool,
408    /// Namespace targets of every `using namespace X;` directive lexically
409    /// visible at this point in the file (declaration order), threaded
410    /// forward sibling-by-sibling by the sequential container walk (see
411    /// `CppWork::Siblings`). An out-of-line member definition written as a
412    /// bare `Class::method` at file/namespace scope with no enclosing
413    /// `namespace {}` block (issue #1093, e.g. log4cxx's
414    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
415    /// const { ... }`) has no other structural signal for which namespace
416    /// actually owns `Class`; this is the best-effort candidate list used to
417    /// recover it so the definition's indexed identity matches its header
418    /// declaration's.
419    visible_using_namespaces: Vec<String>,
420}
421
422struct CppContainer<'tree> {
423    node: Node<'tree>,
424    scope: ScopeInfo,
425}
426
427struct CppNodeWork<'tree> {
428    node: Node<'tree>,
429    scope: ScopeInfo,
430}
431
432/// Cursor over one container's remaining named children, processed one at a
433/// time (rather than all at once) so a `using namespace X;` sibling can
434/// update `scope.visible_using_namespaces` for the siblings that follow it,
435/// matching real C++ using-directive semantics. Nested container work is
436/// still pushed and fully drained before the cursor resumes (stack LIFO
437/// order), preserving the original left-to-right visitation order.
438struct CppSiblingsWork<'tree> {
439    children: std::vec::IntoIter<Node<'tree>>,
440    scope: ScopeInfo,
441}
442
443enum CppWork<'tree> {
444    Container(CppContainer<'tree>),
445    Node(CppNodeWork<'tree>),
446    Siblings(CppSiblingsWork<'tree>),
447}
448
449fn class_like_name<'tree>(
450    node: Node<'tree>,
451    source: &str,
452    ancestry: &ParentIndex<'tree>,
453) -> Option<String> {
454    let best = class_like_name_from_children(node, source);
455    if let Some(parent) = ancestry.parent(node)
456        && matches!(
457            parent.kind(),
458            "declaration" | "field_declaration" | "function_definition"
459        )
460        // A class_specifier carrying its own body proves the grammar name is
461        // the real class name: a sibling declarator then declares an object
462        // (`class X {} x;`), never a displaced class name. The gate matters
463        // when the class name is itself an all-caps token (`X`, `API`) --
464        // without it the export-macro re-read below steals the object
465        // declarator's name for the class (#2283). The genuine export-macro
466        // shapes leave the class_specifier bodyless, the same invariant
467        // recover_malformed_exported_multiple_base_class already gates on.
468        && cpp_body_node(node).is_none()
469        && node
470            .child_by_field_name("name")
471            .map(|name_node| {
472                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
473            })
474            .unwrap_or(false)
475        && let Some(recovered) = exported_class_name_from_node(parent, source)
476        && best.as_deref() != Some(recovered.as_str())
477    {
478        return Some(recovered);
479    }
480    best.or_else(|| {
481        node.child_by_field_name("name")
482            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
483            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
484    })
485}
486
487fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
488    let mut grammar_name = None;
489    if let Some(name_node) = node.child_by_field_name("name") {
490        let name = normalize_cpp_whitespace(node_text(name_node, source));
491        if name.is_empty() {
492            return None;
493        }
494        if !cpp_export_macro_token(&name) {
495            return Some(name);
496        }
497        grammar_name = Some(name);
498    }
499
500    let mut best = None;
501    let mut cursor = node.walk();
502    let mut stack = Vec::new();
503    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
504        if matches!(
505            child.kind(),
506            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
507        ) {
508            break;
509        }
510        stack.push(child);
511    }
512
513    while let Some(current) = stack.pop() {
514        if matches!(current.kind(), "type_identifier" | "identifier") {
515            let name = normalize_cpp_whitespace(node_text(current, source));
516            if !name.is_empty() && !cpp_export_macro_token(&name) {
517                best = Some(name);
518            }
519            continue;
520        }
521
522        push_named_children_reversed(current, &mut stack);
523    }
524    best.or(grammar_name)
525}
526
527pub fn cpp_export_macro_token(token: &str) -> bool {
528    token
529        .chars()
530        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
531}
532
533struct RecoveredExportedClass<'tree> {
534    declaration_node: Node<'tree>,
535    name: String,
536    body: Option<Node<'tree>>,
537    raw_supertypes: Option<Vec<String>>,
538    base_facts: Option<Vec<CppBaseSpecifierFact>>,
539    uses_initializer_body: bool,
540    /// Present only for the fragmented multiple-base export shape (issue #938).
541    /// Carries the true class-body byte region -- the members tree-sitter scattered
542    /// out of the recovered node -- so they can be reparsed and re-owned as members
543    /// rather than lost inside the truncated `initializer_list` stand-in.
544    fragmented_body: Option<FragmentedExportBody>,
545}
546
547struct RecoveredFunctionLikeExportClassPair {
548    name: String,
549    range: Range,
550    raw_supertypes: Option<Vec<String>>,
551    base_facts: Option<Vec<CppBaseSpecifierFact>>,
552    fragmented_body: FragmentedExportBody,
553}
554
555struct RecoveredEmbeddedFunctionLikeExportClass {
556    name: String,
557    range: Range,
558    raw_supertypes: Vec<String>,
559    base_facts: Vec<CppBaseSpecifierFact>,
560    fragmented_body: FragmentedExportBody,
561}
562
563struct RecoveredFunctionLikeExportClass {
564    name: String,
565    range: Range,
566    raw_supertypes: Option<Vec<String>>,
567    base_facts: Option<Vec<CppBaseSpecifierFact>>,
568    member_outcome: Option<FragmentedExportMembers>,
569}
570
571/// The recovered class-body geometry for a fragmented multiple-base export class.
572/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
573/// verbatim for a region reparse (issue #941 machinery) so every recovered member
574/// keeps its exact original byte/line position. `class_range` is the full class
575/// navigation range spanning to the displaced closing brace.
576struct FragmentedExportBody {
577    reparse_start: usize,
578    reparse_end: usize,
579    class_range: Range,
580}
581
582fn recovered_fragmented_export_body(
583    body: Node<'_>,
584    class_range: Range,
585) -> Option<FragmentedExportBody> {
586    let open = body.child(0).filter(|child| child.kind() == "{")?;
587    let close = body
588        .child(body.child_count().saturating_sub(1))
589        .filter(|child| child.kind() == "}" && !child.is_missing());
590    Some(FragmentedExportBody {
591        reparse_start: open.end_byte(),
592        // A zero-width missing `}` contributes no source byte. Keep the whole
593        // body range in that case; subtracting one byte would discard the last
594        // member's semicolon and make the otherwise valid region unsafe to
595        // index. A real close token is excluded by its structured start.
596        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
597        class_range,
598    })
599}
600
601struct DisplacedFragmentNamespaceBoundary<'tree> {
602    class_close: Node<'tree>,
603    class_semicolon: Node<'tree>,
604    namespace_items: Vec<Node<'tree>>,
605}
606
607/// Result of validating a reparsed fragmented class body.  A complete tree can
608/// safely consume the whole region.  A partial tree may contain only the exact
609/// class-named constructor that tree-sitter merged into an access label; its
610/// remaining siblings must stay on the ordinary outer walk.
611enum FragmentedExportMembers {
612    Complete(Tree),
613    ConditionalConstructor(Tree),
614}
615
616#[derive(Clone, Copy)]
617struct DisplacedMacroClassTail {
618    split_index: usize,
619    class_range: Range,
620}
621
622fn recover_exported_class_declaration<'tree>(
623    node: Node<'tree>,
624    source: &str,
625) -> Option<RecoveredExportedClass<'tree>> {
626    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
627        return Some(recovered);
628    }
629
630    let class_node = first_class_like_child(node)?;
631    if let Some(name_node) = class_node.child_by_field_name("name") {
632        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
633        if cpp_export_macro_token(&class_name) {
634            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
635            // Name declarator. Only a bare declarator can be the displaced class name;
636            // wrappers describe an object whose type merely happens to look macro-like.
637            let mut cursor = node.walk();
638            if node
639                .children_by_field_name("declarator", &mut cursor)
640                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
641            {
642                return None;
643            }
644        } else if has_direct_cpp_declarator(node) {
645            return None;
646        }
647    }
648    let name = exported_class_name_from_node(class_node, source)?;
649    Some(RecoveredExportedClass {
650        declaration_node: class_node,
651        name,
652        body: cpp_body_node(class_node),
653        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
654            .then(|| extract_cpp_supertypes(class_node, source)),
655        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
656            .then(|| extract_cpp_base_facts(class_node, source)),
657        uses_initializer_body: false,
658        fragmented_body: None,
659    })
660}
661
662fn recover_malformed_exported_base_class<'tree>(
663    node: Node<'tree>,
664    source: &str,
665) -> Option<RecoveredExportedClass<'tree>> {
666    if node.kind() != "declaration" {
667        return None;
668    }
669    let class_node = node.child_by_field_name("type")?;
670    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
671        return None;
672    }
673    let macro_name = class_node
674        .child_by_field_name("name")
675        .and_then(|name| direct_identifier_name(name, source))?;
676    if !cpp_export_macro_token(&macro_name) {
677        return None;
678    }
679
680    let mut named_cursor = node.walk();
681    let mut named = node.named_children(&mut named_cursor);
682    if named
683        .next()
684        .is_none_or(|child| !same_node(child, class_node))
685    {
686        return None;
687    }
688    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
689    if displaced.kind() != "ERROR" {
690        return None;
691    }
692    let name = displaced_exported_class_name(displaced, source)?;
693
694    let remaining = named.collect::<Vec<_>>();
695    let init = *remaining.last()?;
696    if init.kind() != "init_declarator" {
697        return None;
698    }
699    let final_base = init
700        .child_by_field_name("declarator")
701        .and_then(|base| recovered_malformed_base_name(base, source))?;
702    let body = init.child_by_field_name("value")?;
703    // A complete reduction has a real closing brace here. In Chromium's Widget
704    // declaration, tree-sitter instead emits the same direct `}` slot as a
705    // zero-width missing node where the first body macro truncates the prefix.
706    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
707        return None;
708    }
709
710    if remaining[..remaining.len() - 1]
711        .iter()
712        .any(|child| match child.kind() {
713            "qualified_identifier"
714            | "scoped_type_identifier"
715            | "type_identifier"
716            | "identifier" => false,
717            "ERROR" => !is_malformed_inheritance_access(*child, source),
718            _ => true,
719        })
720    {
721        return None;
722    }
723
724    let mut raw_supertypes = Vec::new();
725    let mut base_facts = Vec::new();
726    for base in &remaining[..remaining.len() - 1] {
727        if base.kind() == "ERROR" {
728            continue;
729        }
730        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
731        base_facts.push(recovered_base_specifier(
732            *base,
733            source,
734            recovered_base_is_virtual(node, *base, source),
735        )?);
736    }
737    raw_supertypes.push(final_base);
738    let final_base_node = init.child_by_field_name("declarator")?;
739    base_facts.push(recovered_base_specifier(
740        final_base_node,
741        source,
742        recovered_base_is_virtual(init, final_base_node, source),
743    )?);
744
745    Some(RecoveredExportedClass {
746        declaration_node: node,
747        name,
748        body: Some(body),
749        raw_supertypes: Some(raw_supertypes),
750        base_facts: Some(base_facts),
751        uses_initializer_body: true,
752        fragmented_body: fragmented_export_body_region(node, body, source),
753    })
754}
755
756/// Locate the true class-body region for a fragmented multiple-base export class.
757///
758/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
759/// emits in place of the real class body. Tree-sitter reduces that body in one of
760/// two shapes, both of which lose the members from the recovered node:
761///
762/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
763///   a real closing brace and holds the whole body text inline. The interior between
764///   the braces reparses to the members directly.
765/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
766///   first member with a zero-width MISSING `}`; every later member -- and the real
767///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
768///   siblings. The interior runs from the opening brace to that displaced `}`.
769///
770/// Returns the interior byte range to reparse plus the full class navigation range.
771fn fragmented_export_body_region(
772    node: Node<'_>,
773    body: Node<'_>,
774    source: &str,
775) -> Option<FragmentedExportBody> {
776    let reparse_start = body.start_byte() + 1;
777    let close = direct_close_brace(body)?;
778    if close.end_byte() > close.start_byte() {
779        return Some(FragmentedExportBody {
780            reparse_start,
781            reparse_end: close.start_byte(),
782            class_range: cpp_declaration_range(node),
783        });
784    }
785    // The closing brace was displaced past the recovered node. A balanced nested
786    // class keeps its own braces, so the first lone-`}` sibling is this class's.
787    let mut sibling = node.next_named_sibling();
788    let displaced_close = loop {
789        let Some(current) = sibling else {
790            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
791        };
792        if cpp_is_stray_close_brace(current, source) {
793            break current;
794        }
795        sibling = current.next_named_sibling();
796    };
797    Some(FragmentedExportBody {
798        reparse_start,
799        reparse_end: displaced_close.start_byte(),
800        class_range: Range {
801            start_byte: node.start_byte(),
802            end_byte: displaced_close.end_byte(),
803            start_line: node.start_position().row + 1,
804            end_line: displaced_close.end_position().row + 1,
805        },
806    })
807}
808
809/// Locate the true class-body region for the export-macro class shape that
810/// tree-sitter promotes to a `function_definition`.
811///
812/// In this shape the synthetic function body closes at the first inline
813/// method, while the class's real members continue as root-level siblings until
814/// a stray `}` followed by the displaced class `;`. Reparse the complete
815/// interior so those siblings are visited with the recovered class scope.
816fn fragmented_export_function_body_region(
817    node: Node<'_>,
818    body: Node<'_>,
819    source: &str,
820    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
821) -> Option<FragmentedExportBody> {
822    let reparse_start = body.start_byte().checked_add(1)?;
823    if let Some(boundary) = displaced_namespace {
824        return Some(FragmentedExportBody {
825            reparse_start,
826            reparse_end: boundary.class_close.start_byte(),
827            class_range: Range {
828                start_byte: node.start_byte(),
829                end_byte: boundary.class_semicolon.end_byte(),
830                start_line: node.start_position().row + 1,
831                end_line: boundary.class_semicolon.end_position().row + 1,
832            },
833        });
834    }
835    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
836    let boundary =
837        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
838    let boundary_index = boundary.and_then(|boundary| {
839        siblings
840            .iter()
841            .position(|candidate| same_node(*candidate, boundary))
842    });
843    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
844    let mut sibling_index = 0;
845    // A complete recovered class's synthetic wrapper is immediately followed
846    // by its displaced semicolon (comments and a trailing attribute macro --
847    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
848    // may sit between the body and that semicolon). Only scan for a later
849    // stray close when real member siblings intervene; otherwise every earlier
850    // complete class would borrow the next malformed class's close and claim
851    // its members. The trailing-attribute case is the gtest shape: the scan
852    // borrowed a close ~1900 lines later and re-owned a following
853    // `namespace testing { namespace internal {` block as class members,
854    // doubling the package path ("testing::internal::testing::internal") and
855    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
856    // boundary assert (#2297).
857    while let Some(current) = siblings.get(sibling_index).copied() {
858        if current.kind() == "comment" {
859            sibling_index += 1;
860            continue;
861        }
862        if is_trailing_attribute_macro_sibling(current) {
863            sibling_index += 1;
864            continue;
865        }
866        if cpp_is_stray_semicolon(current, source) {
867            return None;
868        }
869        break;
870    }
871    while let Some(current) = siblings.get(sibling_index).copied() {
872        let next = siblings.get(sibling_index + 1).copied();
873        if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
874            return Some(FragmentedExportBody {
875                reparse_start,
876                reparse_end: current.start_byte(),
877                class_range: Range {
878                    start_byte: node.start_byte(),
879                    end_byte: semicolon.end_byte(),
880                    start_line: node.start_position().row + 1,
881                    end_line: semicolon.end_position().row + 1,
882                },
883            });
884        }
885        // When the final access label keeps the class close in its malformed
886        // declaration body, tree-sitter nests the lone `}` ERROR below the
887        // label instead of exposing it as a direct sibling. Search only the
888        // scattered siblings after the synthetic wrapper. The first such
889        // close is the class terminator because nested class bodies retain
890        // their own balanced class_specifier nodes.
891        if current.start_byte() >= body.end_byte()
892            && let Some(close) = cpp_nested_stray_close_brace(current, source)
893        {
894            return Some(FragmentedExportBody {
895                reparse_start,
896                reparse_end: close.start_byte(),
897                class_range: Range {
898                    start_byte: node.start_byte(),
899                    end_byte: current.end_byte(),
900                    start_line: node.start_position().row + 1,
901                    end_line: current.end_position().row + 1,
902                },
903            });
904        }
905        sibling_index += 1;
906    }
907    boundary.map(|boundary| FragmentedExportBody {
908        reparse_start,
909        reparse_end: boundary.start_byte(),
910        class_range: Range {
911            start_byte: node.start_byte(),
912            end_byte: boundary.start_byte(),
913            start_line: node.start_position().row + 1,
914            end_line: boundary.start_position().row + 1,
915        },
916    })
917}
918
919/// Find a later macro-export class that tree-sitter lifted through an enclosing
920/// preprocessor container. A class that is still a direct sibling can be a
921/// nested member of the current fragmented class, so only a changed parent is
922/// a proven boundary between the two recovered class envelopes.
923fn fragmented_export_sibling_class_boundary<'tree>(
924    node: Node<'tree>,
925    source: &str,
926    ancestry: &ParentIndex<'tree>,
927) -> Option<Node<'tree>> {
928    let node_parent = ancestry.parent(node)?;
929    cpp_following_named_siblings(node, source, ancestry)
930        .into_iter()
931        .find(|candidate| {
932            recover_exported_class_function_definition(*candidate, source).is_some()
933                && ancestry
934                    .parent(*candidate)
935                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
936        })
937}
938
939/// A trailing attribute macro after a recovered class's closing brace, spelled
940/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
941/// bare identifier is never a class member (members need a type), so this
942/// sibling can only be the class's own tail (#2297).
943fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
944    if node.kind() != "expression_statement" {
945        return false;
946    }
947    let mut cursor = node.walk();
948    let mut children = node.named_children(&mut cursor);
949    children
950        .next()
951        .is_some_and(|child| child.kind() == "identifier")
952        && children.next().is_none()
953}
954
955/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
956/// export-class wrapper can place the class close inside an access-label node,
957/// so direct-sibling checks alone miss the boundary.  Walk named CST children
958/// only; the helper does not inspect source text beyond the existing structured
959/// stray-brace predicate.
960fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
961    let mut stack = vec![node];
962    while let Some(current) = stack.pop() {
963        if cpp_is_stray_close_brace(current, source) {
964            return Some(current);
965        }
966        let mut cursor = current.walk();
967        stack.extend(current.named_children(&mut cursor));
968    }
969    None
970}
971
972/// Return named siblings that follow `node`, including siblings that tree-sitter
973/// attached to an enclosing container after malformed recovery split the local
974/// declaration list. Stop at the first structurally visible class close so a
975/// later namespace or exported class cannot supply the recovery boundary.
976///
977/// The climb takes a [`ParentIndex`] because it runs once per visited node and
978/// walks every level to the root: with `Node::parent`, which re-descends from
979/// the root on each call, a translation unit holding thousands of siblings --
980/// generated instruction tables, for instance -- made this the whole cost of
981/// indexing the file. Callers inside the declaration walk pass the walk's
982/// index; the four that ask from outside a traversal pass
983/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
984/// behaviour is unchanged.
985fn cpp_following_named_siblings<'tree>(
986    node: Node<'tree>,
987    source: &str,
988    ancestry: &ParentIndex<'tree>,
989) -> Vec<Node<'tree>> {
990    let mut siblings = Vec::new();
991    let mut anchor = node;
992    while let Some(parent) = ancestry.parent(anchor) {
993        let at_translation_unit = parent.kind() == "translation_unit";
994        let mut cursor = parent.walk();
995        assert!(
996            cursor.goto_first_child(),
997            "ParentIndex returned a parent without children"
998        );
999        while !same_node(cursor.node(), anchor) {
1000            assert!(
1001                cursor.goto_next_sibling(),
1002                "ParentIndex parent does not contain its child anchor"
1003            );
1004        }
1005        while cursor.goto_next_sibling() {
1006            let current = cursor.node();
1007            if !current.is_named() {
1008                continue;
1009            }
1010            if at_translation_unit
1011                && (current.kind() == "namespace_definition"
1012                    || (current.kind() == "function_definition"
1013                        && first_class_like_child(current).is_some()))
1014            {
1015                return siblings;
1016            }
1017            siblings.push(current);
1018            if cpp_is_stray_close_brace(current, source) {
1019                if let Some(semicolon) = current
1020                    .next_named_sibling()
1021                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1022                {
1023                    siblings.push(semicolon);
1024                }
1025                return siblings;
1026            }
1027            if current.start_byte() >= node.end_byte()
1028                && matches!(current.kind(), "ERROR" | "labeled_statement")
1029                && cpp_nested_stray_close_brace(current, source).is_some()
1030            {
1031                return siblings;
1032            }
1033        }
1034        anchor = parent;
1035    }
1036    siblings
1037}
1038
1039fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1040    if node.start_byte() >= class_end {
1041        return false;
1042    }
1043    node.end_byte() <= class_end
1044        || cpp_nested_stray_close_brace(node, source)
1045            .is_some_and(|close| close.start_byte() == class_end)
1046}
1047
1048/// A class whose head tree-sitter kept in one malformed container while the
1049/// members after the first and the closing `};` scattered across that
1050/// container's following siblings.
1051struct FragmentedClassRecovery<'tree> {
1052    declaration_node: Node<'tree>,
1053    name: String,
1054    raw_supertypes: Vec<String>,
1055    body: FragmentedExportBody,
1056}
1057
1058/// Recover a class whose opening prefix is retained in one ERROR node while one
1059/// or more nested class closes and the outer close are displaced to sibling
1060/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1061/// tokens establish nesting depth and the displaced close nodes terminate it.
1062///
1063/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1064/// `class` keyword as the container's first token. A function-like export macro
1065/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1066/// body-less `class_specifier` named after the macro followed by the
1067/// invocation's `(`, arguments and `)`, with the real class name and the base
1068/// clause left as bare tokens after it (#2924).
1069fn fragmented_class_body<'tree>(
1070    node: Node<'tree>,
1071    source: &str,
1072) -> Option<FragmentedClassRecovery<'tree>> {
1073    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1074        return Some(recovered);
1075    }
1076    let supported_container = node.kind() == "ERROR"
1077        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1078    if !supported_container {
1079        return None;
1080    }
1081    let mut cursor = node.walk();
1082    let children = node.children(&mut cursor).collect::<Vec<_>>();
1083    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1084        return Some(recovered);
1085    }
1086    let keyword = children.first()?;
1087    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1088        return None;
1089    }
1090    let name_node = children
1091        .iter()
1092        .copied()
1093        .skip(1)
1094        .find(|child| child.is_named())?;
1095    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1096        return None;
1097    }
1098    let name = normalize_cpp_whitespace(node_text(name_node, source));
1099    if name.is_empty() || cpp_export_macro_token(&name) {
1100        return None;
1101    }
1102    let open_index = children.iter().position(|child| child.kind() == "{")?;
1103    Some(FragmentedClassRecovery {
1104        declaration_node: node,
1105        name,
1106        raw_supertypes: extract_cpp_supertypes(node, source),
1107        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
1108    })
1109}
1110
1111/// The fragmented body of a function-like export-macro class head that
1112/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1113/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1114/// public virtual EC_PublicKey {`, #2924).
1115///
1116/// The class name is read positionally by [`recovered_export_head_name`] -- the
1117/// last identifier before the head ends at `final`, at the base clause `:`, or
1118/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1119/// spelled with `virtual`, with `final`, or with qualified base names needs no
1120/// arm of its own.
1121fn fragmented_export_macro_class_body<'tree>(
1122    node: Node<'tree>,
1123    children: &[Node<'tree>],
1124    source: &str,
1125) -> Option<FragmentedClassRecovery<'tree>> {
1126    let class_node = *children.first()?;
1127    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1128        return None;
1129    }
1130    // The identifier tree-sitter took for the class name is the export macro, a
1131    // function-like invocation when the `(` of its argument list follows it
1132    // directly. Spelling plays no part (#2557).
1133    class_node
1134        .child_by_field_name("name")
1135        .and_then(|name| direct_identifier_name(name, source))?;
1136    let invocation = *children.get(1)?;
1137    if invocation.is_named() || invocation.kind() != "(" {
1138        return None;
1139    }
1140    let open_index = children
1141        .iter()
1142        .position(|child| !child.is_named() && child.kind() == "{")?;
1143    let open = children[open_index];
1144    let name_node = recovered_export_head_name(node, open, source)?;
1145    let name = normalize_cpp_whitespace(node_text(name_node, source));
1146    if name.is_empty() || cpp_export_macro_token(&name) {
1147        return None;
1148    }
1149    Some(FragmentedClassRecovery {
1150        declaration_node: node,
1151        name,
1152        raw_supertypes: recovered_export_head_bases(
1153            node,
1154            name_node.end_byte(),
1155            open.start_byte(),
1156            source,
1157        ),
1158        body: fragmented_displaced_class_body(node, children, open_index, source)
1159            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1160    })
1161}
1162
1163/// The class body a fragmented head opened at `children[open_index]` but could
1164/// not close: tree-sitter scattered the remaining members and the closing `};`
1165/// across the container's following siblings. The direct class tokens after the
1166/// opening brace say how many closes the nested classes consume before this
1167/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1168fn fragmented_displaced_class_body(
1169    node: Node<'_>,
1170    children: &[Node<'_>],
1171    open_index: usize,
1172    source: &str,
1173) -> Option<FragmentedExportBody> {
1174    let open = children[open_index];
1175    let nested_class_opens = children[open_index + 1..]
1176        .iter()
1177        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1178        .count();
1179    let mut closes_remaining = 1 + nested_class_opens;
1180    let mut sibling = node.next_named_sibling();
1181    while let Some(candidate) = sibling {
1182        let next = candidate.next_named_sibling();
1183        if cpp_is_stray_close_brace(candidate, source) {
1184            closes_remaining -= 1;
1185            if closes_remaining == 0 {
1186                let semicolon = cpp_stray_close_followed_by_semicolon(candidate, next, source)?;
1187                if open.end_byte() >= candidate.start_byte() {
1188                    return None;
1189                }
1190                return Some(FragmentedExportBody {
1191                    reparse_start: open.end_byte(),
1192                    reparse_end: candidate.start_byte(),
1193                    class_range: Range {
1194                        start_byte: node.start_byte(),
1195                        end_byte: semicolon.end_byte(),
1196                        start_line: node.start_position().row + 1,
1197                        end_line: semicolon.end_position().row + 1,
1198                    },
1199                });
1200            }
1201        }
1202        sibling = next;
1203    }
1204    None
1205}
1206
1207/// The body of a fragmented export-macro class head whose close tree-sitter
1208/// spent on the enclosing container instead: Botan's
1209/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1210/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1211/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1212/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1213/// (#2924).
1214fn fragmented_container_close_class_body(
1215    node: Node<'_>,
1216    open: Node<'_>,
1217    source: &str,
1218) -> Option<FragmentedExportBody> {
1219    let parent = node.parent()?;
1220    if !matches!(
1221        parent.kind(),
1222        "declaration_list" | "field_declaration_list" | "compound_statement"
1223    ) {
1224        return None;
1225    }
1226    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1227    // The container's close is this class's close only when the body's own
1228    // brace balance says so. Without that cross-check -- the one issue #1524
1229    // already uses on a mis-closed wrapper body -- a class that really does end
1230    // earlier would claim every later container-level declaration as a member.
1231    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1232        || open.end_byte() >= close.start_byte()
1233    {
1234        return None;
1235    }
1236    Some(FragmentedExportBody {
1237        reparse_start: open.end_byte(),
1238        reparse_end: close.start_byte(),
1239        class_range: Range {
1240            start_byte: node.start_byte(),
1241            end_byte: close.end_byte(),
1242            start_line: node.start_position().row + 1,
1243            end_line: close.end_position().row + 1,
1244        },
1245    })
1246}
1247
1248pub(crate) fn recovered_fragmented_class_has_body(
1249    node: Node<'_>,
1250    source: &str,
1251    expected_name: &str,
1252    expected_range: &Range,
1253) -> bool {
1254    fragmented_class_body(node, source).is_some_and(|recovered| {
1255        recovered.name == expected_name
1256            && recovered.body.class_range.start_byte == expected_range.start_byte
1257            && recovered.body.class_range.end_byte == expected_range.end_byte
1258    })
1259}
1260
1261/// Recover a plain class whose parser-visible body ends inside a malformed
1262/// inline member. Tree-sitter then attaches either the next real member
1263/// declarator or the unfinished `else` branch directly to the outer function
1264/// definition and leaves the class's actual `};` among later siblings. Those
1265/// structured continuations and the close/semicolon siblings establish the
1266/// complete body envelope without interpreting source text.
1267fn fragmented_plain_class_declaration_body<'tree>(
1268    node: Node<'tree>,
1269    source: &str,
1270) -> Option<FragmentedClassRecovery<'tree>> {
1271    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1272        return None;
1273    }
1274    let class_node = node.child_by_field_name("type")?;
1275    if !matches!(
1276        class_node.kind(),
1277        "class_specifier" | "struct_specifier" | "union_specifier"
1278    ) {
1279        return None;
1280    }
1281    let name_node = class_node.child_by_field_name("name")?;
1282    let name = normalize_cpp_whitespace(node_text(name_node, source));
1283    if name.is_empty() || cpp_export_macro_token(&name) {
1284        return None;
1285    }
1286    let body = cpp_body_node(class_node)?;
1287    if body.kind() != "field_declaration_list" {
1288        return None;
1289    }
1290    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1291        if declarator.start_byte() < class_node.end_byte() {
1292            return None;
1293        }
1294        let mut cursor = node.walk();
1295        node.named_children(&mut cursor).any(|child| {
1296            if child.kind() != "ERROR"
1297                || child.start_byte() < class_node.end_byte()
1298                || child.end_byte() > declarator.start_byte()
1299            {
1300                return false;
1301            }
1302            let mut cursor = child.walk();
1303            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1304            let Some((return_type, attributes)) = components.split_last() else {
1305                return false;
1306            };
1307            matches!(
1308                return_type.kind(),
1309                "identifier"
1310                    | "type_identifier"
1311                    | "primitive_type"
1312                    | "decltype"
1313                    | "placeholder_type_specifier"
1314            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1315                && attributes.iter().all(|attribute| {
1316                    matches!(attribute.kind(), "identifier" | "type_identifier")
1317                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1318                            *attribute, source,
1319                        )))
1320                })
1321        })
1322    } else {
1323        let mut cursor = node.walk();
1324        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1325        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1326            if same_node(*candidate_class, class_node)
1327                && continuation.kind() == "identifier"
1328                && node_text(*continuation, source) == "else"
1329                && continuation_body.kind() == "compound_statement"
1330                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1331                && continuation_body
1332                    .child(continuation_body.child_count().saturating_sub(1))
1333                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1334    };
1335    if !displaced_member {
1336        return None;
1337    }
1338    let open = body
1339        .children(&mut body.walk())
1340        .find(|child| child.kind() == "{")?;
1341    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1342    let ordinary_boundary =
1343        siblings
1344            .iter()
1345            .copied()
1346            .enumerate()
1347            .find_map(|(close_index, close)| {
1348                cpp_stray_close_followed_by_semicolon(
1349                    close,
1350                    siblings.get(close_index + 1).copied(),
1351                    source,
1352                )
1353                .map(|semicolon| (close, semicolon))
1354            });
1355    let (close, semicolon) =
1356        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1357            (boundary.class_close, boundary.class_semicolon)
1358        } else {
1359            ordinary_boundary?
1360        };
1361    if open.end_byte() >= close.start_byte() {
1362        return None;
1363    }
1364    Some(FragmentedClassRecovery {
1365        declaration_node: class_node,
1366        name,
1367        raw_supertypes: extract_cpp_supertypes(class_node, source),
1368        body: FragmentedExportBody {
1369            reparse_start: open.end_byte(),
1370            reparse_end: close.start_byte(),
1371            class_range: Range {
1372                start_byte: class_node.start_byte(),
1373                end_byte: semicolon.end_byte(),
1374                start_line: class_node.start_position().row + 1,
1375                end_line: semicolon.end_position().row + 1,
1376            },
1377        },
1378    })
1379}
1380
1381fn displaced_export_function_namespace_shape<'tree>(
1382    declaration: Node<'tree>,
1383    source: &str,
1384) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1385    let mut nested = Vec::new();
1386    for index in (0..declaration.named_child_count()).rev() {
1387        nested.push(declaration.named_child(index)?);
1388    }
1389    while let Some(current) = nested.pop() {
1390        // A recovered export class nested in this class can consume the first
1391        // parser-visible namespace close itself. In that shape the existing
1392        // later-class boundary logic already distinguishes the nested and
1393        // namespace-sibling owners; do not mistake the nested close for this
1394        // class's terminator.
1395        if recover_exported_class_function_definition(current, source).is_some() {
1396            return None;
1397        }
1398        for index in (0..current.named_child_count()).rev() {
1399            nested.push(current.named_child(index)?);
1400        }
1401    }
1402    let mut same_envelope_sibling = declaration.next_named_sibling();
1403    while let Some(current) = same_envelope_sibling {
1404        if recover_exported_class_function_definition(current, source).is_some() {
1405            return None;
1406        }
1407        same_envelope_sibling = current.next_named_sibling();
1408    }
1409    let declaration_list = declaration.parent()?;
1410    let (namespace, class_close, trailing_semicolon) =
1411        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1412    // A chain of malformed export classes can consume one parser-visible
1413    // namespace close per class. Walk through the enclosing sibling levels so
1414    // the later real namespace close remains the structural boundary; a
1415    // direct next-sibling walk stops at the first collapsed namespace and
1416    // incorrectly makes its intervening items members of this class.
1417    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1418    let trailing_index = siblings
1419        .iter()
1420        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1421    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1422        recover_exported_class_function_definition(*candidate, source).is_some()
1423    }) {
1424        // Consecutive recovered classes already have an exact sibling-class
1425        // boundary. Preserve that established path, including nested export
1426        // classes, instead of interpreting the first class close as a
1427        // collapsed namespace boundary.
1428        return None;
1429    }
1430    let mut namespace_items = Vec::new();
1431    let mut nested_fragment_end = 0;
1432    for current in siblings.into_iter().skip(trailing_index + 1) {
1433        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1434        {
1435            return Some(DisplacedFragmentNamespaceBoundary {
1436                class_close,
1437                class_semicolon: trailing_semicolon,
1438                namespace_items,
1439            });
1440        }
1441        if current.start_byte() >= nested_fragment_end
1442            && let Some(recovered) = fragmented_class_body(current, source)
1443        {
1444            nested_fragment_end = recovered.body.class_range.end_byte;
1445        } else if current.start_byte() >= nested_fragment_end
1446            && recover_exported_class_function_definition(current, source).is_some()
1447            && let Some(body) = cpp_body_node(current)
1448            && let Some(fragmented) =
1449                fragmented_export_function_body_region(current, body, source, None)
1450        {
1451            nested_fragment_end = fragmented.class_range.end_byte;
1452        }
1453        namespace_items.push(current);
1454    }
1455    None
1456}
1457
1458fn displaced_fragment_namespace_boundary<'tree>(
1459    declaration: Node<'tree>,
1460    body: Node<'tree>,
1461    source: &str,
1462) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1463    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1464    let reparse_start = body.start_byte() + 1;
1465    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1466    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1467}
1468
1469/// Recover the class/namespace brace geometry for a declaration whose class
1470/// close tree-sitter consumed as the enclosing namespace close. This proof is
1471/// independent of whether every member in the class body can be reparsed: the
1472/// ordinary-tree fallback can still re-own bounded sibling declarations when
1473/// an unknown macro makes the complete body reparse unsafe.
1474fn displaced_fragment_namespace_geometry<'tree>(
1475    declaration: Node<'tree>,
1476    source: &str,
1477) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1478    // A templated class's malformed function wrapper remains beneath the
1479    // template node even though its later members have escaped to the
1480    // enclosing declaration list. Lift only that exact declaration child.
1481    let envelope = declaration
1482        .parent()
1483        .filter(|parent| {
1484            parent.kind() == "template_declaration"
1485                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1486        })
1487        .unwrap_or(declaration);
1488    let declaration_list = envelope.parent()?;
1489    let (_, class_close, trailing_semicolon) =
1490        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1491    let mut namespace_items = Vec::new();
1492    let mut sibling = trailing_semicolon.next_named_sibling();
1493    let mut nested_fragment_end = 0;
1494    loop {
1495        let current = sibling?;
1496        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1497        {
1498            break;
1499        }
1500        if current.start_byte() >= nested_fragment_end
1501            && let Some(recovered) = fragmented_class_body(current, source)
1502        {
1503            nested_fragment_end = recovered.body.class_range.end_byte;
1504        }
1505        namespace_items.push(current);
1506        sibling = current.next_named_sibling();
1507    }
1508    Some(DisplacedFragmentNamespaceBoundary {
1509        class_close,
1510        class_semicolon: trailing_semicolon,
1511        namespace_items,
1512    })
1513}
1514
1515fn cpp_fragment_namespace_boundary_nodes<'tree>(
1516    declaration_list: Node<'tree>,
1517) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1518    if declaration_list.kind() != "declaration_list" {
1519        return None;
1520    }
1521    let namespace = declaration_list.parent()?;
1522    if namespace.kind() != "namespace_definition"
1523        || namespace.child_by_field_name("body") != Some(declaration_list)
1524    {
1525        return None;
1526    }
1527    let class_close = direct_close_brace(declaration_list)?;
1528    let trailing_semicolon = namespace.next_named_sibling()?;
1529    (trailing_semicolon.kind() == "expression_statement"
1530        && trailing_semicolon.named_child_count() == 0)
1531        .then_some((namespace, class_close, trailing_semicolon))
1532}
1533
1534/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1535fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1536    (0..node.child_count())
1537        .filter_map(|index| node.child(index))
1538        .find(|child| !child.is_named() && child.kind() == "}")
1539}
1540
1541/// A displaced lone closing brace: the class close that the fragmented multiple-base
1542/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1543fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1544    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1545}
1546
1547/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1548/// text while skipping line/block comments and string/char literals. The
1549/// exported-class recovery needs this when tree-sitter's bogus
1550/// `function_definition` body runs past the class's true closing brace and
1551/// swallows following siblings (issue #1524): the grammar tree carries no
1552/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1553/// close is located textually. Returns `None` when the text is unbalanced or
1554/// contains a construct the scanner deliberately does not interpret (raw
1555/// strings) -- callers treat that as "cannot partition" and keep the
1556/// un-split recovery.
1557fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1558    let bytes = source.as_bytes();
1559    if bytes.get(open_byte) != Some(&b'{') {
1560        return None;
1561    }
1562    let mut depth = 0usize;
1563    let mut i = open_byte;
1564    while i < bytes.len() {
1565        match bytes[i] {
1566            b'{' => depth += 1,
1567            b'}' => {
1568                depth = depth.checked_sub(1)?;
1569                if depth == 0 {
1570                    return Some(i);
1571                }
1572            }
1573            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1574                while i < bytes.len() && bytes[i] != b'\n' {
1575                    i += 1;
1576                }
1577                continue;
1578            }
1579            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1580                i += 2;
1581                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1582                    i += 1;
1583                }
1584                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1585                continue;
1586            }
1587            quote @ (b'"' | b'\'') => {
1588                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1589                // bail out rather than mis-count.
1590                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1591                    return None;
1592                }
1593                i += 1;
1594                while i < bytes.len() && bytes[i] != quote {
1595                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1596                }
1597                if i >= bytes.len() {
1598                    return None;
1599                }
1600            }
1601            _ => {}
1602        }
1603        i += 1;
1604    }
1605    None
1606}
1607
1608fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1609    let mut name = None;
1610    let mut colon_count = 0;
1611    let mut access_count = 0;
1612    for index in 0..node.child_count() {
1613        let child = node.child(index)?;
1614        match child.kind() {
1615            "identifier" | "type_identifier" if child.is_named() => {
1616                if name.is_some() {
1617                    return None;
1618                }
1619                let candidate = normalize_cpp_whitespace(node_text(child, source));
1620                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1621                    return None;
1622                }
1623                name = Some(candidate);
1624            }
1625            "template_function" | "template_type" if child.is_named() => {
1626                if name.is_some() {
1627                    return None;
1628                }
1629                let candidate = child
1630                    .child_by_field_name("name")
1631                    .and_then(|name| direct_identifier_name(name, source))?;
1632                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1633                    return None;
1634                }
1635                name = Some(candidate);
1636            }
1637            ":" if !child.is_named() => colon_count += 1,
1638            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1639            _ => return None,
1640        }
1641    }
1642    (colon_count == 1 && access_count == 1)
1643        .then_some(name)
1644        .flatten()
1645}
1646
1647fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1648    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1649        return false;
1650    }
1651    node.named_child(0)
1652        .and_then(|child| direct_identifier_name(child, source))
1653        .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1654}
1655
1656use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1657
1658fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1659    match node.kind() {
1660        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1661            recovered_base_atom(node, source)
1662        }
1663        "template_type" | "template_function" => node
1664            .child_by_field_name("name")
1665            .and_then(|name| recovered_malformed_base_name(name, source)),
1666        "ERROR" => None,
1667        "qualified_identifier" | "scoped_type_identifier" => {
1668            let suffix = node
1669                .child_by_field_name("name")
1670                .and_then(|name| recovered_malformed_base_name(name, source))?;
1671            let scope = node
1672                .child_by_field_name("scope")
1673                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1674            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1675                malformed_qualified_prefix(node, source)?
1676            } else {
1677                if malformed_qualified_prefix(node, source).is_some() {
1678                    return None;
1679                }
1680                scope
1681            };
1682            Some(format!("{prefix}::{suffix}"))
1683        }
1684        _ => None,
1685    }
1686}
1687
1688fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1689    let mut pending = vec![node];
1690    let mut components = Vec::new();
1691    while let Some(current) = pending.pop() {
1692        match current.kind() {
1693            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1694                components.push(recovered_base_atom(current, source)?);
1695            }
1696            "template_type" | "template_function" => {
1697                pending.push(current.child_by_field_name("name")?);
1698            }
1699            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1700                pending.push(current.child_by_field_name("name")?);
1701                if let Some(scope) = current.child_by_field_name("scope") {
1702                    // Malformed export bases can put the access keyword in
1703                    // `scope` and the real scope in the direct ERROR child.
1704                    // Keep the exact AST recovery used by the declaration.
1705                    if recovered_base_atom(scope, source).is_some_and(|name| {
1706                        matches!(name.as_str(), "public" | "protected" | "private")
1707                    }) {
1708                        pending.push(malformed_qualified_prefix_node(current, source)?);
1709                    } else {
1710                        if malformed_qualified_prefix_node(current, source).is_some() {
1711                            return None;
1712                        }
1713                        pending.push(scope);
1714                    }
1715                }
1716            }
1717            _ => return None,
1718        }
1719    }
1720    Some(components)
1721}
1722
1723fn recovered_base_specifier(
1724    node: Node<'_>,
1725    source: &str,
1726    is_virtual: bool,
1727) -> Option<CppBaseSpecifierFact> {
1728    let components = recovered_base_components(node, source)?;
1729    crate::source_facts::base_specifier_fact(
1730        node,
1731        components,
1732        is_virtual,
1733        CppAccessSpecifier::Unknown,
1734    )
1735}
1736
1737fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1738    // Recovery can put the qualifier in an ERROR sibling or inside the base
1739    // node. Read only the AST prefix through this base, resetting at preceding
1740    // inheritance delimiters so another base's qualifier cannot leak here.
1741    let mut pending = vec![container];
1742    let mut is_virtual = false;
1743    while let Some(node) = pending.pop() {
1744        if node.start_byte() >= base.end_byte() {
1745            break;
1746        }
1747        if node.child_count() != 0 {
1748            let mut cursor = node.walk();
1749            let children = node
1750                .children(&mut cursor)
1751                .take_while(|child| child.start_byte() < base.end_byte())
1752                .collect::<Vec<_>>();
1753            pending.extend(children.into_iter().rev());
1754            continue;
1755        }
1756        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1757            is_virtual = false;
1758        } else if node.kind() == "virtual"
1759            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1760        {
1761            is_virtual = true;
1762        }
1763    }
1764    is_virtual
1765}
1766
1767fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1768    if !matches!(
1769        node.kind(),
1770        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1771    ) {
1772        return None;
1773    }
1774    let name = normalize_cpp_whitespace(node_text(node, source));
1775    (!name.is_empty()).then_some(name)
1776}
1777
1778fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1779    malformed_qualified_prefix_node(node, source)
1780        .and_then(|prefix| recovered_base_atom(prefix, source))
1781}
1782
1783fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1784    let mut prefix = None;
1785    let mut cursor = node.walk();
1786    for error in node
1787        .named_children(&mut cursor)
1788        .filter(|child| child.kind() == "ERROR")
1789    {
1790        if prefix.is_some() {
1791            return None;
1792        }
1793        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1794        // the qualified name's scope and leaves `virtual` beside the real
1795        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1796        // component of the base's name (#2924).
1797        let mut error_cursor = error.walk();
1798        let atoms = error
1799            .named_children(&mut error_cursor)
1800            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1801            .collect::<Option<Vec<_>>>()?;
1802        let [atom] = atoms
1803            .iter()
1804            .filter(|(_, name)| name != "virtual")
1805            .collect::<Vec<_>>()[..]
1806        else {
1807            return None;
1808        };
1809        prefix = Some(atom.0);
1810    }
1811    prefix
1812}
1813
1814type RecoveredFunctionDefinitionClass<'tree> = (
1815    Node<'tree>,
1816    String,
1817    Option<Vec<String>>,
1818    Option<Vec<CppBaseSpecifierFact>>,
1819);
1820/// One declaration an attribute-like macro invocation swallowed into a
1821/// declaration-scope `ERROR`, with the byte range that spells it.
1822/// What [`stranded_declaration_run`] read out of one node.
1823struct StrandedRun<'tree> {
1824    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1825    /// Whether every part of the node read as part of a declaration. False when
1826    /// a part the reader does not understand ended it early, or when the last
1827    /// parts were types with no declarator after them. Callers that index what
1828    /// was found keep the declarations either way; a caller deciding whether a
1829    /// whole region is safe to index requires this.
1830    complete: bool,
1831}
1832
1833struct MacroWrappedDeclaration<'tree> {
1834    declarator: Node<'tree>,
1835    range: Range,
1836    /// Whether the recovered declaration spells `static`. The envelope hides
1837    /// that keyword from the ordinary linkage reader, which looks for it among
1838    /// a declaration node's own children, and internal linkage is what decides
1839    /// whether a header declaration and a body in another file are one symbol.
1840    is_static: bool,
1841}
1842
1843/// Whether `node` stands where declarations live: directly in the translation
1844/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1845/// stands in one of those.
1846///
1847/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1848/// where it stands. The case that matters is a whole translation unit the
1849/// parser could not close: whisper.cpp's `include/whisper.h` opens
1850/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1851/// so the braces never balance in the token stream tree-sitter sees, the file's
1852/// own root node is an `ERROR`, and every top-level declaration in the file is
1853/// a child of it (#3094). Without this the macro recoveries below read nothing
1854/// in such a file, because none of their envelopes is in a declaration scope
1855/// the parser managed to build.
1856fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1857    declaration_scope_container(node, ancestry).is_some()
1858}
1859
1860/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1861/// end is where a parse failure that starts at `node` can still be doing
1862/// damage: a declaration scope has no synchronization point of its own, so the
1863/// parser carries such a failure to the scope's close.
1864fn declaration_scope_container<'tree>(
1865    node: Node<'tree>,
1866    ancestry: &ParentIndex<'tree>,
1867) -> Option<Node<'tree>> {
1868    let mut parent = ancestry.parent(node)?;
1869    loop {
1870        match parent.kind() {
1871            "translation_unit" => return Some(parent),
1872            "declaration_list" => {
1873                return ancestry
1874                    .parent(parent)
1875                    .is_some_and(|grandparent| {
1876                        matches!(
1877                            grandparent.kind(),
1878                            "namespace_definition" | "linkage_specification"
1879                        )
1880                    })
1881                    .then_some(parent);
1882            }
1883            "ERROR" => match ancestry.parent(parent) {
1884                Some(grandparent) => parent = grandparent,
1885                // A root `ERROR` is the translation unit the parser could not
1886                // build, so its children stand at file scope.
1887                None => return Some(parent),
1888            },
1889            // A preprocessor conditional is a transparent declaration-scope
1890            // wrapper when it sits directly under the translation unit or a
1891            // namespace/linkage declaration list. Keep climbing through it;
1892            // the next ordinary construct still decides whether the enclosed
1893            // error is at declaration scope. In particular, a conditional
1894            // inside a function reaches its compound statement and is
1895            // rejected below rather than becoming a global recovery region.
1896            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
1897                parent = ancestry.parent(parent)?;
1898            }
1899            _ => return None,
1900        }
1901    }
1902}
1903
1904/// Whether `node` is an `ERROR` the parser produced where declarations live.
1905fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1906    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1907}
1908
1909/// Whether `node` can only be part of what precedes a declarator -- a type, a
1910/// specifier, or a word of the attribute macro's own text that the lexer left
1911/// as a bare identifier -- so a run of these followed by a declarator is one
1912/// declaration the parser failed to group.
1913fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1914    matches!(
1915        node.kind(),
1916        "identifier"
1917            | "type_identifier"
1918            | "primitive_type"
1919            | "sized_type_specifier"
1920            | "struct_specifier"
1921            | "union_specifier"
1922            | "enum_specifier"
1923            | "type_qualifier"
1924            | "storage_class_specifier"
1925            | "explicit_function_specifier"
1926            | "virtual_function_specifier"
1927            | "qualified_identifier"
1928            | "template_type"
1929            | "dependent_type"
1930            | "placeholder_type_specifier"
1931    )
1932}
1933
1934/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1935/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1936/// words the lexer reports as bare identifiers. Anything else in such an
1937/// `ERROR` stops the recovery, so a macro argument that carries structure this
1938/// recovery does not understand is never guessed at.
1939fn is_macro_argument_error(node: Node<'_>) -> bool {
1940    if node.kind() != "ERROR" {
1941        return false;
1942    }
1943    let mut cursor = node.walk();
1944    node.named_children(&mut cursor).all(|child| {
1945        matches!(
1946            child.kind(),
1947            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1948        )
1949    })
1950}
1951
1952/// The end of a recovered declaration: the declarator's end, extended across
1953/// the `;` the grammar left beside it inside the envelope.
1954///
1955/// The envelope's own end is the hard boundary. The parser hands the last
1956/// declaration's `;` to the sibling statement it recovered with, outside the
1957/// envelope, and a range that reached it would no longer lie inside one node --
1958/// which is how every reader (including the resolver's climb from a range to
1959/// the node that declares it) finds a recovered declaration again.
1960fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1961    declarator
1962        .next_sibling()
1963        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1964        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1965}
1966
1967/// The declarations `node` holds after an attribute-like macro cost the parser
1968/// their grouping, in source order.
1969///
1970/// The parser packs the parts into whichever slots it has left. In whisper's
1971/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1972/// field takes the export macro, the real return type and the *next*
1973/// declaration's declarator both end up inside one sibling `ERROR`, and the
1974/// `declarator` field takes whatever declaration the recovery reached last. In
1975/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1976/// arrive as bare identifiers and the attributed member and the member after it
1977/// share one declarator node.
1978///
1979/// Both are the same failure, so both get the same reading: flatten the node's
1980/// parts -- splicing each nested `ERROR`'s own children in place, since an
1981/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1982/// spells, a run of type-and-specifier tokens followed by a declarator, over
1983/// and over.
1984///
1985/// Fails closed. A part that is neither a declarator nor something that can
1986/// only precede one ends the recovery there, and the declarations before it are
1987/// kept.
1988fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1989    let mut parts = Vec::new();
1990    let mut cursor = node.walk();
1991    for child in node.named_children(&mut cursor) {
1992        if child.kind() == "ERROR" {
1993            let mut error_cursor = child.walk();
1994            parts.extend(child.named_children(&mut error_cursor));
1995        } else {
1996            parts.push(child);
1997        }
1998    }
1999
2000    let mut declarations = Vec::new();
2001    let mut start = None;
2002    let mut is_static = false;
2003    let mut complete = true;
2004    for part in parts {
2005        if part.kind() == "comment" {
2006            continue;
2007        }
2008        if let Some(declarator) = extract_function_declarator(part) {
2009            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2010            declarations.push(MacroWrappedDeclaration {
2011                declarator,
2012                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2013                is_static,
2014            });
2015            is_static = false;
2016            continue;
2017        }
2018        if !is_recovered_declaration_type_part(part) {
2019            complete = false;
2020            break;
2021        }
2022        is_static |= part.kind() == "storage_class_specifier"
2023            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2024        start.get_or_insert(part.start_byte());
2025    }
2026    StrandedRun {
2027        declarations,
2028        complete: complete && start.is_none(),
2029    }
2030}
2031
2032/// The declarations an attribute-like macro invocation swallowed into a
2033/// declaration-scope `ERROR`, in source order.
2034///
2035/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2036/// declaration in a macro call:
2037///
2038/// ```text
2039/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2040///                  struct llama_model * model,
2041///           struct llama_context_params   params),
2042///         "use llama_init_from_model instead");
2043/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2044/// ```
2045///
2046/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2047/// holding the macro name, the wrapped declaration as a
2048/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2049/// following declaration as a further `parameter_declaration` until it
2050/// recovers. That is what removed `llama_tokenize` from the index and left the
2051/// seven-argument call in `talk-llama.cpp` with only that file's own
2052/// three-parameter `static` overload to choose from (#2552, and the
2053/// `LLAMA_API` half of #2551).
2054///
2055/// Every part of every swallowed declaration is still a real node; only their
2056/// grouping is lost. This rebuilds the grouping and reads the nodes.
2057fn macro_wrapped_declarations<'tree>(
2058    envelope: Node<'tree>,
2059    source: &str,
2060    ancestry: &ParentIndex<'tree>,
2061) -> Vec<MacroWrappedDeclaration<'tree>> {
2062    let mut declarations = Vec::new();
2063    if !is_declaration_scope_error(envelope, ancestry) {
2064        return declarations;
2065    }
2066    let mut cursor = envelope.walk();
2067    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2068    let [macro_name, arguments @ ..] = children.as_slice() else {
2069        return declarations;
2070    };
2071    if macro_name.kind() != "identifier" {
2072        return declarations;
2073    }
2074    let mut wrapped_declaration_seen = false;
2075    for argument in arguments {
2076        match argument.kind() {
2077            "comment" => {}
2078            "parameter_declaration" => {
2079                let recovered = stranded_declaration_run(*argument, source).declarations;
2080                if recovered.is_empty() {
2081                    break;
2082                }
2083                wrapped_declaration_seen = true;
2084                declarations.extend(recovered);
2085            }
2086            // The hint string, and only that: an argument the recovery cannot
2087            // read as a declaration is admitted before the wrapped declaration
2088            // is found, so a macro whose first argument is not a declaration
2089            // recovers nothing.
2090            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
2091            _ => break,
2092        }
2093    }
2094    declarations
2095}
2096
2097/// What one macro invocation swallowed when it collapsed a whole run of
2098/// declarations.
2099struct CollapsedMacroDeclarationRun {
2100    /// The byte just past the `;` that closes the invocation, which is where
2101    /// the declarations it swallowed begin.
2102    invocation_end: usize,
2103    /// The byte this recovery owns to: the close of the enclosing declaration
2104    /// scope when the invocation swallowed the declarations written after it,
2105    /// and the invocation's own end when the parser instead spread the
2106    /// invocation itself across the container's children and swallowed nothing.
2107    region_end: usize,
2108}
2109
2110/// The tokens one collapsed macro invocation spans, in source order: the leaves
2111/// of `node`, then the leaves of the siblings written after it. Comments and
2112/// the parser's own MISSING tokens are skipped; neither is in the source the
2113/// invocation is written in.
2114///
2115/// The parser puts the parts of a failed invocation wherever it has room. It
2116/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2117/// `expression_statement` it recovered with; it spreads them flat over the
2118/// container's children; it packs the tail of the file into one of the
2119/// arguments. The grouping therefore says nothing, but the token order still
2120/// spells the invocation, which is what this reads.
2121struct MacroInvocationTokens<'tree> {
2122    stack: Vec<Node<'tree>>,
2123    /// The last sibling handed to `stack`, and the point the next one is asked
2124    /// for. Held as the node rather than as its already-computed successor so
2125    /// the successor is asked for only when the walk actually runs off the end
2126    /// of what it has: `Node::next_sibling` recovers the parent first, which
2127    /// re-descends from the root, so it costs the node's position in the tree.
2128    /// Nearly every node this iterator is built for is rejected on its first
2129    /// token, before any sibling is needed, and computing one eagerly made that
2130    /// rejection cost a walk of the whole container (capstone's generated
2131    /// instruction tables, t19).
2132    frontier: Option<Node<'tree>>,
2133}
2134
2135impl<'tree> MacroInvocationTokens<'tree> {
2136    fn new(node: Node<'tree>) -> Self {
2137        Self {
2138            stack: vec![node],
2139            frontier: Some(node),
2140        }
2141    }
2142}
2143
2144impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2145    type Item = Node<'tree>;
2146
2147    fn next(&mut self) -> Option<Node<'tree>> {
2148        loop {
2149            let Some(node) = self.stack.pop() else {
2150                let sibling = self.frontier?.next_sibling()?;
2151                self.frontier = Some(sibling);
2152                self.stack.push(sibling);
2153                continue;
2154            };
2155            if node.child_count() == 0 {
2156                if node.kind() == "comment" || node.is_missing() {
2157                    continue;
2158                }
2159                return Some(node);
2160            }
2161            let mut cursor = node.walk();
2162            let children = node.children(&mut cursor).collect::<Vec<_>>();
2163            self.stack.extend(children.into_iter().rev());
2164        }
2165    }
2166}
2167
2168/// The macro invocation that begins at `node` when the parser could not group
2169/// it, or `None` when `node` does not begin one.
2170///
2171/// whisper.cpp's bundled `llama.h` writes
2172///
2173/// ```text
2174/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2175///                          const char * path_model,
2176///           struct llama_model_params   params),
2177///         "use llama_model_load_from_file instead");
2178/// ```
2179///
2180/// and its own `include/whisper.h` writes
2181///
2182/// ```text
2183/// WHISPER_DEPRECATED(
2184///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2185///     "use whisper_init_from_file_with_params instead"
2186/// );
2187/// ```
2188///
2189/// tree-sitter cannot know either name is a macro, so it reads the name and its
2190/// `(` as a function declarator whose close it never finds, and carries that
2191/// failure forward over the declarations written after it. In `llama.h` that
2192/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2193/// (#2551). In `whisper.h` it instead flattens the invocations over the
2194/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2195/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2196/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2197/// `function_definition` when a brace block falls in a swallowed tail, because
2198/// the parser borrows it for the body the bogus definition needs, and an
2199/// `ERROR` or a bare token when none does. None of that says anything about the
2200/// construct, so the node's kind is not the criterion; the token order is.
2201///
2202/// The invocation ends at the `)` that closes its own `(`, immediately followed
2203/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2204/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2205/// its own, so a `;` reached before the close means this is some other
2206/// construct -- a macro-defined function body, say -- and the ordinary readers
2207/// keep the node.
2208///
2209/// Fails closed everywhere else too. The head must be an export-macro-shaped
2210/// token followed by `(`, the node must stand where declarations live, and an
2211/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2212/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2213/// nothing past it -- is left to that reader.
2214fn collapsed_macro_declaration_run<'tree>(
2215    node: Node<'tree>,
2216    source: &str,
2217    ancestry: &ParentIndex<'tree>,
2218) -> Option<CollapsedMacroDeclarationRun> {
2219    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2220    let mut tokens = MacroInvocationTokens::new(node);
2221    let name = tokens.next()?;
2222    if !matches!(name.kind(), "identifier" | "type_identifier")
2223        || !cpp_export_macro_token(node_text(name, source))
2224    {
2225        return None;
2226    }
2227    if tokens.next()?.kind() != "(" {
2228        return None;
2229    }
2230    let mut depth = 1usize;
2231    let invocation_end = loop {
2232        let token = tokens.next()?;
2233        match token.kind() {
2234            "(" => depth += 1,
2235            ";" => return None,
2236            ")" => {
2237                depth -= 1;
2238                if depth == 0 {
2239                    let semicolon = tokens.next()?;
2240                    if semicolon.kind() != ";" {
2241                        return None;
2242                    }
2243                    break semicolon.end_byte();
2244                }
2245            }
2246            _ => {}
2247        }
2248    };
2249    // An invocation that reaches the end of its own declaration scope swallowed
2250    // nothing: there is nothing after it left to recover, and the ordinary
2251    // readers have the invocation itself. This is also what stops the scan
2252    // below from re-entering itself, since the region it reparses for one
2253    // invocation ends exactly at that invocation's `;`.
2254    if invocation_end >= container_end {
2255        return None;
2256    }
2257    // Where the damage ends. A node that reaches past its own invocation packed
2258    // real declarations into the invocation's argument list, so the parser was
2259    // still failing when it got there and everything to the close of the scope
2260    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2261    // line 455 with it, and every declaration from there to the end of the file
2262    // is shredded. A node that stops at or before the invocation's `;` holds
2263    // nothing but the invocation, whose own parts the parser spread over the
2264    // siblings after it.
2265    let region_end = if node.end_byte() > invocation_end {
2266        container_end
2267    } else {
2268        invocation_end
2269    };
2270    Some(CollapsedMacroDeclarationRun {
2271        invocation_end,
2272        region_end,
2273    })
2274}
2275
2276/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2277/// ordinary class body, and the members it swallowed.
2278///
2279/// Botan deprecates members that way:
2280///
2281/// ```text
2282/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2283/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2284/// ```
2285///
2286/// The parser reads the macro name as the member's type and its argument list
2287/// as a parenthesized declarator, which then swallows the attributed member
2288/// *and* the member written after it: a `field_declaration` whose `type` is a
2289/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2290/// opening with an `ERROR` whose first part is a bare identifier -- the first
2291/// word of the string, which the lexer could not keep together.
2292///
2293/// The macro's spelling is not the criterion; that structural shape is. The
2294/// returned declarations are in source order, the first being the attributed
2295/// member and the rest the members the declarator swallowed after it.
2296fn string_attribute_macro_member_declarators<'tree>(
2297    field: Node<'tree>,
2298    source: &str,
2299) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2300    if field.kind() != "field_declaration"
2301        || field
2302            .child_by_field_name("type")
2303            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2304    {
2305        return None;
2306    }
2307    let declarator = field.child_by_field_name("declarator")?;
2308    if declarator.kind() != "parenthesized_declarator" {
2309        return None;
2310    }
2311    let opening = declarator.named_child(0)?;
2312    if opening.kind() != "ERROR"
2313        || opening
2314            .named_child(0)
2315            .is_none_or(|word| word.kind() != "identifier")
2316    {
2317        return None;
2318    }
2319    let declarations = stranded_declaration_run(declarator, source).declarations;
2320    (!declarations.is_empty()).then_some(declarations)
2321}
2322
2323/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2324/// export-macro class body leaves in front of the members that macro decorated.
2325///
2326/// The reparse reads the class body as statements, so the attribute becomes a
2327/// call statement of its own -- with the `;` the grammar had to invent -- and
2328/// the members after it are stranded in the `ERROR` that follows.
2329fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2330    let Some(call) = (node.kind() == "expression_statement")
2331        .then(|| node.named_child(0))
2332        .flatten()
2333        .filter(|child| child.kind() == "call_expression")
2334    else {
2335        return false;
2336    };
2337    call.child_by_field_name("function")
2338        .is_some_and(|function| function.kind() == "identifier")
2339        && call
2340            .child_by_field_name("arguments")
2341            .is_some_and(|arguments| {
2342                let mut cursor = arguments.walk();
2343                arguments.named_child_count() > 0
2344                    && arguments
2345                        .named_children(&mut cursor)
2346                        .all(|argument| argument.kind() == "string_literal")
2347            })
2348}
2349
2350/// The start byte of the call the region reparse left where an access-labeled
2351/// constructor was written.
2352///
2353/// A constructor is a member only inside a class body. The export-macro class
2354/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2355/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2356/// comma-expression under `private:`, with no declarator left in the tree. It
2357/// is still spelled in the source, though, starting at the one call under the
2358/// label whose callee is the class's own name. Botan's private six-parameter
2359/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2360/// call is an ambiguity this declines, and so is a reparse from that byte that
2361/// does not yield exactly one constructor declarator there.
2362fn cpp_access_label_constructor_call_start(
2363    node: Node<'_>,
2364    class_name: &str,
2365    source: &str,
2366) -> Option<usize> {
2367    if node.kind() != "labeled_statement" {
2368        return None;
2369    }
2370    let label = node.named_child(0)?;
2371    if label.kind() != "statement_identifier"
2372        || !matches!(
2373            node_text(label, source).trim(),
2374            "public" | "private" | "protected"
2375        )
2376    {
2377        return None;
2378    }
2379    let mut starts = Vec::new();
2380    let mut stack = vec![node];
2381    while let Some(current) = stack.pop() {
2382        if current.kind() == "call_expression"
2383            && current
2384                .child_by_field_name("function")
2385                .is_some_and(|function| {
2386                    function.kind() == "identifier"
2387                        && node_text(function, source).trim() == class_name
2388                })
2389        {
2390            starts.push(current.start_byte());
2391        }
2392        let mut cursor = current.walk();
2393        stack.extend(current.named_children(&mut cursor));
2394    }
2395    let [start] = starts.as_slice() else {
2396        return None;
2397    };
2398    Some(*start)
2399}
2400
2401/// The `function_definition` that gives `declarator` a body, following only the
2402/// declarator chain, so a recovered callable knows whether it is a declaration
2403/// or a definition without anyone having to say.
2404fn cpp_declarator_function_definition<'tree>(
2405    declarator: Node<'tree>,
2406    ancestry: &ParentIndex<'tree>,
2407) -> Option<Node<'tree>> {
2408    let mut current = declarator;
2409    while let Some(parent) = ancestry.parent(current) {
2410        match parent.kind() {
2411            "function_definition" if parent.child_by_field_name("body").is_some() => {
2412                return Some(parent);
2413            }
2414            "pointer_declarator"
2415            | "reference_declarator"
2416            | "parenthesized_declarator"
2417            | "array_declarator" => current = parent,
2418            _ => return None,
2419        }
2420    }
2421    None
2422}
2423
2424/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2425/// declaration walk still reaches, so a recovery that runs ahead of that walk
2426/// would name what it finds without the namespace.
2427fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2428    let mut current = node;
2429    while let Some(parent) = ancestry.parent(current) {
2430        if parent.kind() == "namespace_definition"
2431            && parent.child_by_field_name("body") == Some(current)
2432        {
2433            return true;
2434        }
2435        current = parent;
2436    }
2437    false
2438}
2439
2440/// Whether the source a recovered callable's byte range spells is a definition
2441/// (a declarator with a body) rather than a declaration.
2442///
2443/// A callable recovered from a mangled region owns no node of its own in the
2444/// file's tree, so the occurrence-role scan that climbs from the range to the
2445/// node containing it lands on whatever container the parser left and answers
2446/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2447/// constructor a declaration and left its call site with nothing to navigate to
2448/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2449/// extraction used, and read the answer from the one declaration it spells.
2450///
2451/// `None` when the range is not one recovered declaration on its own, which is
2452/// the case for every ordinary declaration, so callers keep their own answer.
2453pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2454    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2455    let root = tree.root_node();
2456    let mut cursor = root.walk();
2457    let items = root
2458        .named_children(&mut cursor)
2459        .filter(|child| child.kind() != "comment")
2460        .collect::<Vec<_>>();
2461    let [item] = items.as_slice() else {
2462        return None;
2463    };
2464    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2465        return None;
2466    }
2467    match item.kind() {
2468        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2469        "declaration" | "field_declaration" => Some(false),
2470        _ => None,
2471    }
2472}
2473
2474/// Whether `node` is an envelope an attribute-like macro invocation left where
2475/// declarations were written: the declaration-scope `ERROR`
2476/// [`macro_wrapped_declarations`] reads, or the collapsed run
2477/// [`collapsed_macro_declaration_run`] reads.
2478///
2479/// Extraction and resolution must read one definition of this shape. The
2480/// resolver climbs from a declaration's recorded byte range to the node that
2481/// declares it, and a recovered declaration's range lies inside one of these
2482/// envelopes rather than inside a `declaration` node, so the climb stops here
2483/// (the same role `is_recovered_exported_class_container` plays for a recovered
2484/// class).
2485pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2486    let ancestry = ParentIndex::unindexed();
2487    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2488        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2489}
2490
2491fn recover_exported_class_function_definition<'tree>(
2492    node: Node<'tree>,
2493    source: &str,
2494) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2495    if node.kind() != "function_definition" {
2496        return None;
2497    }
2498    if let Some(prefix) = node.prev_named_sibling()
2499        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2500        && recovered.range.end_byte == node.end_byte()
2501    {
2502        return Some((
2503            node,
2504            recovered.name,
2505            recovered.raw_supertypes,
2506            recovered.base_facts,
2507        ));
2508    }
2509    let type_node = node.child_by_field_name("type")?;
2510    let declarator = node.child_by_field_name("declarator")?;
2511
2512    if matches!(
2513        type_node.kind(),
2514        "class_specifier" | "struct_specifier" | "union_specifier"
2515    ) {
2516        let type_name = type_node
2517            .child_by_field_name("name")
2518            .and_then(|name| direct_identifier_name(name, source));
2519        let exported_macro_type = type_name
2520            .as_ref()
2521            .is_some_and(|name| cpp_export_macro_token(name));
2522        if exported_macro_type {
2523            let mut cursor = node.walk();
2524            let errors_before_declarator = node
2525                .named_children(&mut cursor)
2526                .filter(|child| {
2527                    child.kind() == "ERROR"
2528                        && child.start_byte() >= type_node.end_byte()
2529                        && child.end_byte() <= declarator.start_byte()
2530                })
2531                .collect::<Vec<_>>();
2532            if let Some(name) = errors_before_declarator
2533                .iter()
2534                .find_map(|error| displaced_exported_class_name(*error, source))
2535            {
2536                let base_node = errors_before_declarator
2537                    .iter()
2538                    .any(|error| malformed_inheritance_syntax(*error))
2539                    .then_some(declarator);
2540                let raw_supertypes = base_node
2541                    .and_then(|base| recovered_malformed_base_name(base, source))
2542                    .map(|base| vec![base]);
2543                let base_facts = base_node
2544                    .and_then(|base| {
2545                        recovered_base_specifier(
2546                            base,
2547                            source,
2548                            recovered_base_is_virtual(node, base, source),
2549                        )
2550                    })
2551                    .map(|base| vec![base]);
2552                return Some((node, name, raw_supertypes, base_facts));
2553            }
2554            if errors_before_declarator
2555                .iter()
2556                .any(|error| malformed_inheritance_syntax(*error))
2557            {
2558                return None;
2559            }
2560        }
2561        if !exported_macro_type
2562            && let Some(name) = type_name
2563            && !cpp_export_macro_token(&name)
2564            && let Some(base) =
2565                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2566        {
2567            let base_node =
2568                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2569            let base_facts = Some(vec![recovered_base_specifier(
2570                base_node,
2571                source,
2572                recovered_base_is_virtual(node, base_node, source),
2573            )?]);
2574            return Some((node, name, Some(vec![base]), base_facts));
2575        }
2576        if let Some(name) = direct_identifier_name(declarator, source)
2577            && exported_macro_type
2578            && !cpp_export_macro_token(&name)
2579        {
2580            let base_node = exported_macro_type
2581                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2582                .flatten();
2583            let raw_supertypes = base_node
2584                .and_then(|base| recovered_malformed_base_name(base, source))
2585                .map(|base| vec![base]);
2586            let base_facts = base_node
2587                .and_then(|base| {
2588                    recovered_base_specifier(
2589                        base,
2590                        source,
2591                        recovered_base_is_virtual(node, base, source),
2592                    )
2593                })
2594                .map(|base| vec![base]);
2595            return Some((node, name, raw_supertypes, base_facts));
2596        }
2597        if declarator.kind() == "parenthesized_declarator"
2598            && type_node
2599                .child_by_field_name("name")
2600                .and_then(|name| direct_identifier_name(name, source))
2601                .is_some_and(|name| cpp_export_macro_token(&name))
2602        {
2603            if let Some((name, base_node)) =
2604                recovered_function_like_export_class_owner_node(declarator, source)
2605            {
2606                return Some((
2607                    node,
2608                    name,
2609                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2610                    Some(vec![recovered_base_specifier(
2611                        base_node,
2612                        source,
2613                        recovered_base_is_virtual(node, base_node, source),
2614                    )?]),
2615                ));
2616            }
2617            let body_start = node
2618                .child_by_field_name("body")
2619                .map(|body| body.start_byte())
2620                .unwrap_or(node.end_byte());
2621            let mut cursor = node.walk();
2622            if let Some(name) = node
2623                .named_children(&mut cursor)
2624                .filter(|child| {
2625                    child.kind() == "ERROR"
2626                        && child.start_byte() >= declarator.end_byte()
2627                        && child.end_byte() <= body_start
2628                })
2629                .find_map(|error| declarator_name_from_node(error, source))
2630            {
2631                return Some((node, name, None, None));
2632            }
2633        }
2634    }
2635
2636    let declarator_text = direct_identifier_name(declarator, source)?;
2637    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2638        return None;
2639    }
2640    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2641}
2642
2643fn recovered_function_like_export_class_owner_node<'tree>(
2644    declarator: Node<'tree>,
2645    source: &str,
2646) -> Option<(String, Node<'tree>)> {
2647    if declarator.kind() != "parenthesized_declarator" {
2648        return None;
2649    }
2650    let mut cursor = declarator.walk();
2651    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2652    let [prefix, base] = children.as_slice() else {
2653        return None;
2654    };
2655    if prefix.kind() != "ERROR"
2656        || !matches!(
2657            base.kind(),
2658            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2659        )
2660    {
2661        return None;
2662    }
2663    let mut identifiers = Vec::new();
2664    let mut prefix_cursor = prefix.walk();
2665    for child in prefix.named_children(&mut prefix_cursor) {
2666        match child.kind() {
2667            "number_literal" | "string_literal" | "char_literal" => {}
2668            "identifier" | "type_identifier" => {
2669                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2670            }
2671            _ => return None,
2672        }
2673    }
2674    let name = match identifiers.as_slice() {
2675        [name] => name.clone(),
2676        [name, final_token] if final_token == "final" => name.clone(),
2677        _ => return None,
2678    };
2679    if name.is_empty() || cpp_export_macro_token(&name) {
2680        return None;
2681    }
2682    Some((name, *base))
2683}
2684
2685/// Collect the base names and source facts tree-sitter scattered across the
2686/// recovered head of a function-like export-macro class. A base arrives either
2687/// as a direct sibling identifier or inside the `ERROR` node the grammar
2688/// produced for a `: public Base` fragment. The grammar leaves the `final`
2689/// specifier and access specifiers in the same position as the bases, so drop
2690/// them.
2691/// The bases a recovered function-like export-macro class head names between
2692/// `after` (the end of the class name, or of the access specifier that stands
2693/// in for it) and `before` (the start of the body, or of the `init_declarator`
2694/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2695/// fragments, and one fragment can also hold the class name and `final`
2696/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2697/// than by the fragment that holds it.
2698fn recovered_export_head_base_data(
2699    node: Node<'_>,
2700    after: usize,
2701    before: usize,
2702    source: &str,
2703) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
2704    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2705    let mut base_nodes = Vec::new();
2706    let mut cursor = node.walk();
2707    for child in node.named_children(&mut cursor) {
2708        if child.kind() == "ERROR" {
2709            let mut error_cursor = child.walk();
2710            base_nodes.extend(
2711                child
2712                    .named_children(&mut error_cursor)
2713                    .filter(within)
2714                    .filter_map(|part| {
2715                        recovered_malformed_base_name(part, source).map(|_| (part, child))
2716                    }),
2717            );
2718        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
2719            base_nodes.push((child, node));
2720        }
2721    }
2722    let mut bases = Vec::new();
2723    let mut facts = Vec::new();
2724    for (base, container) in base_nodes {
2725        let Some(name) = recovered_malformed_base_name(base, source) else {
2726            continue;
2727        };
2728        if matches!(
2729            name.as_str(),
2730            "final" | "public" | "protected" | "private" | "virtual"
2731        ) {
2732            continue;
2733        }
2734        bases.push(name);
2735        if let Some(fact) = recovered_base_specifier(
2736            base,
2737            source,
2738            recovered_base_is_virtual(container, base, source),
2739        ) {
2740            facts.push(fact);
2741        }
2742    }
2743    (bases, facts)
2744}
2745
2746fn recovered_export_head_bases(
2747    node: Node<'_>,
2748    after: usize,
2749    before: usize,
2750    source: &str,
2751) -> Vec<String> {
2752    recovered_export_head_base_data(node, after, before, source).0
2753}
2754
2755/// Whether `token` is the `final` that closes a recovered class head. The
2756/// grammar keeps it as the `final` keyword when it recognised the head
2757/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2758/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2759fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2760    if token.is_named() {
2761        token.kind() == "identifier" && node_text(token, source) == "final"
2762    } else {
2763        token.kind() == "final"
2764    }
2765}
2766
2767/// The class name of a recovered function-like export-macro class head, read by
2768/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2769/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2770/// carries the body. The head's tokens, in source order with `ERROR` fragments
2771/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2772/// identifier before the head ends, and the head ends at `final`, at the base
2773/// clause `:`, or at `tail`. Every identifier before the name is decoration
2774/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2775/// (`X509_CA`) is a class name like any other.
2776///
2777/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2778/// rule, not a token of the source, so it is never the name.
2779fn recovered_export_head_name<'tree>(
2780    node: Node<'tree>,
2781    tail: Node<'tree>,
2782    source: &str,
2783) -> Option<Node<'tree>> {
2784    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2785}
2786
2787/// The class name of a head whose tokens straddle the recovered pair: the
2788/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2789/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2790/// The positional rule is the same, read over the two token runs in source
2791/// order, so a name in either node wins by position rather than by which node
2792/// holds it.
2793fn recovered_export_pair_head_name<'tree>(
2794    prefix: Node<'tree>,
2795    sibling: Node<'tree>,
2796    tail: Node<'tree>,
2797    source: &str,
2798) -> Option<Node<'tree>> {
2799    let mut tokens = export_head_tokens(prefix, None);
2800    tokens.extend(export_head_tokens(sibling, Some(tail)));
2801    export_head_name_from_tokens(&tokens, source)
2802}
2803
2804/// One node's head tokens in source order, with `ERROR` fragments spliced in
2805/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2806/// before `tail` when the caller names one.
2807fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2808    let mut tokens = Vec::new();
2809    let mut cursor = node.walk();
2810    for child in node.children(&mut cursor) {
2811        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2812            break;
2813        }
2814        if child.kind() == "ERROR" {
2815            let mut fragment_cursor = child.walk();
2816            tokens.extend(child.children(&mut fragment_cursor));
2817        } else {
2818            tokens.push(child);
2819        }
2820    }
2821    tokens
2822}
2823
2824/// The last identifier before the head ends, per the positional rule.
2825fn export_head_name_from_tokens<'tree>(
2826    tokens: &[Node<'tree>],
2827    source: &str,
2828) -> Option<Node<'tree>> {
2829    let end = tokens
2830        .iter()
2831        .position(|token| {
2832            recovered_export_head_final(*token, source)
2833                || (!token.is_named() && token.kind() == ":")
2834        })
2835        .unwrap_or(tokens.len());
2836    export_head_last_identifier(tokens[..end].iter().copied())
2837}
2838
2839fn export_head_last_identifier<'tree>(
2840    candidates: impl IntoIterator<Item = Node<'tree>>,
2841) -> Option<Node<'tree>> {
2842    candidates
2843        .into_iter()
2844        .filter(|candidate| {
2845            candidate.is_named()
2846                && !candidate.is_missing()
2847                && matches!(
2848                    candidate.kind(),
2849                    "identifier" | "type_identifier" | "field_identifier"
2850                )
2851        })
2852        .last()
2853}
2854
2855/// The `init_declarator` whose `value` carries the class body when the grammar
2856/// keeps a function-like export-macro class head as one `declaration`.
2857fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2858    let mut cursor = declaration.walk();
2859    declaration
2860        .named_children(&mut cursor)
2861        .find(|child| child.kind() == "init_declarator")
2862}
2863
2864/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2865/// of a function-like export-macro class head. The grammar splits a base list
2866/// across bare declarator fields, `ERROR` fragments, and one trailing
2867/// `init_declarator` whose `value` is the class body. `head_end` is where the
2868/// class identity ends and the bases can begin: the end of the access specifier
2869/// when the class name went to a statement label, and the end of the class name
2870/// itself otherwise.
2871fn recovered_export_declaration_tail<'tree>(
2872    declaration: Node<'tree>,
2873    head_end: usize,
2874    source: &str,
2875) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
2876    let init = recovered_export_init_declarator(declaration)?;
2877    let body = init.child_by_field_name("value")?;
2878    if body.kind() != "initializer_list" {
2879        return None;
2880    }
2881    let (mut bases, mut facts) =
2882        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
2883    let (more_bases, more_facts) =
2884        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
2885    bases.extend(more_bases);
2886    facts.extend(more_facts);
2887    Some((bases, (!facts.is_empty()).then_some(facts), body))
2888}
2889
2890/// Whether `node` is the head fragment tree-sitter leaves where
2891/// `class MACRO(args)` was written: the class keyword and the export macro's
2892/// function-like invocation in that order, with nothing else between them.
2893/// Spelling plays no part -- the invocation's `(` following the macro
2894/// identifier directly is what makes it function-like (#2557).
2895///
2896/// Two reductions produce the head. On its own the grammar keeps a body-less
2897/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2898/// with the invocation's `(`, arguments and `)` beside it. After object-like
2899/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2900/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2901/// `declaration` whose class keyword is demoted to a bare `identifier` and
2902/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2903fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2904    match node.kind() {
2905        "ERROR" => {
2906            let Some(class_node) = first_class_like_child(node) else {
2907                return false;
2908            };
2909            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2910                return false;
2911            }
2912            if class_node
2913                .child_by_field_name("name")
2914                .and_then(|name| direct_identifier_name(name, source))
2915                .is_none()
2916            {
2917                return false;
2918            }
2919            class_node
2920                .next_sibling()
2921                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2922        }
2923        "declaration" => {
2924            let mut cursor = node.walk();
2925            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2926            // The class keyword lost its own node: it survives as a bare
2927            // identifier spelled `class`, `struct` or `union`, which is never a
2928            // declarator or a type in well-formed code. It can sit inside an
2929            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2930            // a grouping failure rather than a construct, so read through it.
2931            let Some(keyword) = children
2932                .iter()
2933                .copied()
2934                .flat_map(|child| {
2935                    let mut cursor = child.walk();
2936                    if child.kind() == "ERROR" {
2937                        child.named_children(&mut cursor).collect::<Vec<_>>()
2938                    } else {
2939                        vec![child]
2940                    }
2941                })
2942                .find(|child| {
2943                    child.kind() == "identifier"
2944                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2945                })
2946            else {
2947                return false;
2948            };
2949            children.last().is_some_and(|init| {
2950                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2951                    let mut cursor = init.walk();
2952                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2953                    matches!(parts.as_slice(), [macro_name, arguments]
2954                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2955                            && arguments.kind() == "argument_list")
2956                }
2957            })
2958        }
2959        _ => false,
2960    }
2961}
2962
2963fn recover_function_like_export_class_pair(
2964    node: Node<'_>,
2965    source: &str,
2966) -> Option<RecoveredFunctionLikeExportClassPair> {
2967    if !is_function_like_export_class_head(node, source) {
2968        return None;
2969    }
2970    let sibling = node.next_named_sibling()?;
2971    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
2972        // At translation-unit scope tree-sitter can leave the malformed class
2973        // head in one ERROR node and parse its body as the adjacent compound
2974        // statement. The head still carries the export invocation, displaced
2975        // class name, and optional `final` token in source order, so recover
2976        // the name by the same positional rule as the other shapes.
2977        "compound_statement" => (
2978            recovered_export_pair_head_name(node, sibling, sibling, source)
2979                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2980            None,
2981            None,
2982            sibling,
2983        ),
2984        "expression_statement" => {
2985            let compound = sibling.named_child(0)?;
2986            if compound.kind() != "compound_literal_expression" {
2987                return None;
2988            }
2989            let body = compound.child_by_field_name("value")?;
2990            if body.kind() != "initializer_list" {
2991                return None;
2992            }
2993            (
2994                compound
2995                    .child_by_field_name("type")
2996                    .and_then(|name| direct_identifier_name(name, source))?,
2997                None,
2998                None,
2999                body,
3000            )
3001        }
3002        "labeled_statement" => {
3003            let label = sibling.child_by_field_name("label")?;
3004            if label.kind() != "statement_identifier" {
3005                return None;
3006            }
3007            let name = normalize_cpp_whitespace(node_text(label, source));
3008            let declaration = sibling
3009                .named_children(&mut sibling.walk())
3010                .find(|child| child.kind() == "declaration")?;
3011            let access = declaration.child_by_field_name("type")?;
3012            if !matches!(
3013                node_text(access, source),
3014                "public" | "protected" | "private"
3015            ) {
3016                return None;
3017            }
3018            let (bases, base_facts, body) =
3019                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3020            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3021        }
3022        // `class MACRO(2, 0) Name final { ... };`,
3023        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3024        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3025        // spread over the `type` field, the declarator field, and `ERROR`
3026        // fragments beside them; the name is the last one before the head ends.
3027        "function_definition" => {
3028            let body = sibling.child_by_field_name("body")?;
3029            if body.kind() != "compound_statement" {
3030                return None;
3031            }
3032            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3033            let (bases, base_facts) = recovered_export_head_base_data(
3034                sibling,
3035                name_node.end_byte(),
3036                body.start_byte(),
3037                source,
3038            );
3039            (
3040                normalize_cpp_whitespace(node_text(name_node, source)),
3041                (!bases.is_empty()).then_some(bases),
3042                (!base_facts.is_empty()).then_some(base_facts),
3043                body,
3044            )
3045        }
3046        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3047        // comma-separated base list makes the grammar keep the whole tail as one
3048        // declaration whose trailing `init_declarator` carries the body.
3049        "declaration" => {
3050            let init = recovered_export_init_declarator(sibling)?;
3051            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3052            let (bases, base_facts, body) =
3053                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3054            (
3055                normalize_cpp_whitespace(node_text(name_node, source)),
3056                (!bases.is_empty()).then_some(bases),
3057                base_facts,
3058                body,
3059            )
3060        }
3061        _ => return None,
3062    };
3063    debug_assert!(
3064        !name.is_empty(),
3065        "a recovered class head names its class by an identifier token"
3066    );
3067    let range = Range {
3068        start_byte: node.start_byte(),
3069        end_byte: sibling.end_byte(),
3070        start_line: node.start_position().row + 1,
3071        end_line: sibling.end_position().row + 1,
3072    };
3073    Some(RecoveredFunctionLikeExportClassPair {
3074        name,
3075        raw_supertypes,
3076        base_facts,
3077        range,
3078        fragmented_body: recovered_fragmented_export_body(body, range)?,
3079    })
3080}
3081
3082/// Recover a function-like export-macro class that tree-sitter embedded in a
3083/// larger error after an earlier malformed class body. The grammar still
3084/// preserves every part of the class head: the `class` token, export macro
3085/// identifier and argument list, displaced class identifier, access specifier,
3086/// base field, and initializer-list-shaped body. Match only that complete
3087/// structured sequence and keep each recovered class's exact byte envelope.
3088fn recover_embedded_function_like_export_classes(
3089    node: Node<'_>,
3090    source: &str,
3091) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3092    if !node.is_error() {
3093        return Vec::new();
3094    }
3095
3096    let mut nodes = Vec::new();
3097    let mut class_tokens = Vec::new();
3098    let mut stack = vec![node];
3099    while let Some(current) = stack.pop() {
3100        nodes.push(current);
3101        // An error subtree without an anonymous class token cannot contain
3102        // a recoverable class head. Detect that before sorting or resolving
3103        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3104        if !current.is_named() && current.kind() == "class" {
3105            class_tokens.push(current);
3106        }
3107        push_children_reversed(current, &mut stack);
3108    }
3109    if class_tokens.is_empty() {
3110        return Vec::new();
3111    }
3112    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3113    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3114
3115    // The scans below ask the same handful of kind questions once per node of
3116    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3117    let language = node.language();
3118    let class_kind = NodeKindIds::new(&language, "class");
3119    let identifier_kinds = [
3120        NodeKindIds::new(&language, "identifier"),
3121        NodeKindIds::new(&language, "type_identifier"),
3122        NodeKindIds::new(&language, "field_identifier"),
3123    ];
3124    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3125    let colon_kind = NodeKindIds::new(&language, ":");
3126    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3127
3128    let mut recovered = Vec::new();
3129    for class_token in class_tokens {
3130        debug_assert!(class_kind.matches(class_token));
3131        let row = class_token.start_position().row;
3132        // The head after the `class` token reads `MACRO(args) macros... name
3133        // [final] : bases`. The macro is the first identifier, a function-like
3134        // invocation when its argument list is the next thing after it, and the
3135        // name is the last identifier before the head ends at `final` or at the
3136        // base clause `:`. Spelling plays no part (#2557).
3137        let is_identifier = |candidate: &Node<'_>| {
3138            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3139        };
3140        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3141            candidate.start_byte() >= class_token.end_byte()
3142                && candidate.start_position().row == row
3143                && is_identifier(candidate)
3144        }) else {
3145            continue;
3146        };
3147        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3148            argument_list_kind.matches(*candidate)
3149                && candidate.start_byte() >= macro_name.end_byte()
3150                && candidate.start_position().row == row
3151        }) else {
3152            continue;
3153        };
3154        if nodes.iter().any(|candidate| {
3155            is_identifier(candidate)
3156                && candidate.start_byte() >= macro_name.end_byte()
3157                && candidate.end_byte() <= arguments.start_byte()
3158        }) {
3159            continue;
3160        }
3161        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3162            candidate.start_byte() >= arguments.end_byte()
3163                && (recovered_export_head_final(*candidate, source)
3164                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3165        }) else {
3166            continue;
3167        };
3168        let Some(name_node) =
3169            export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3170                is_identifier(candidate)
3171                    && candidate.start_byte() >= arguments.end_byte()
3172                    && candidate.end_byte() <= head_end.start_byte()
3173                    && candidate.start_position().row == row
3174            }))
3175        else {
3176            continue;
3177        };
3178        let name = normalize_cpp_whitespace(node_text(name_node, source));
3179        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3180            field_initializer_kind.matches(*candidate)
3181                && candidate.start_byte() >= name_node.end_byte()
3182                && candidate
3183                    .child_by_field_name("field")
3184                    .or_else(|| candidate.named_child(0))
3185                    .is_some()
3186                && candidate
3187                    .child_by_field_name("value")
3188                    .or_else(|| {
3189                        let mut cursor = candidate.walk();
3190                        candidate
3191                            .named_children(&mut cursor)
3192                            .find(|child| child.kind() == "initializer_list")
3193                    })
3194                    .is_some_and(|value| value.kind() == "initializer_list")
3195        }) else {
3196            continue;
3197        };
3198        let has_access = nodes.iter().copied().any(|candidate| {
3199            candidate.start_byte() >= name_node.end_byte()
3200                && candidate.end_byte() <= base_initializer.start_byte()
3201                && matches!(
3202                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3203                    "public" | "protected" | "private"
3204                )
3205        });
3206        if !has_access {
3207            continue;
3208        }
3209        let Some(base_node) = base_initializer
3210            .child_by_field_name("field")
3211            .or_else(|| base_initializer.named_child(0))
3212        else {
3213            continue;
3214        };
3215        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3216            continue;
3217        };
3218        let body = base_initializer
3219            .child_by_field_name("value")
3220            .or_else(|| {
3221                let mut cursor = base_initializer.walk();
3222                base_initializer
3223                    .named_children(&mut cursor)
3224                    .find(|child| child.kind() == "initializer_list")
3225            })
3226            .expect("initializer-list value checked above");
3227        let range = Range {
3228            start_byte: class_token.start_byte(),
3229            end_byte: body.end_byte(),
3230            start_line: class_token.start_position().row + 1,
3231            end_line: body.end_position().row + 1,
3232        };
3233        if recovered
3234            .iter()
3235            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3236                existing.name == name && existing.range == range
3237            })
3238        {
3239            continue;
3240        }
3241        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3242            continue;
3243        };
3244        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3245            name,
3246            range,
3247            raw_supertypes: vec![base],
3248            base_facts: vec![base_fact],
3249            fragmented_body: match recovered_fragmented_export_body(body, range) {
3250                Some(fragmented) => fragmented,
3251                None => continue,
3252            },
3253        });
3254    }
3255    recovered
3256}
3257
3258fn lifted_function_like_export_class_namespace<'tree>(
3259    node: Node<'tree>,
3260    source: &str,
3261    ancestry: &ParentIndex<'tree>,
3262) -> Option<String> {
3263    // A long malformed body can embed the next exported class several levels
3264    // below a bogus top-level function_definition. Compare namespace evidence
3265    // against that top-level envelope, not only the recovered ERROR's direct
3266    // parent. The source tree still proves the same boundary: one earlier
3267    // malformed namespace and one later standalone closing brace.
3268    let mut anchor = node;
3269    let parent = loop {
3270        let parent = ancestry.parent(anchor)?;
3271        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3272            break parent;
3273        }
3274        anchor = parent;
3275    };
3276    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3277        sibling.start_byte() > anchor.end_byte()
3278            && sibling.kind() == "ERROR"
3279            && sibling.named_child_count() == 0
3280            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3281    });
3282    if !has_later_close {
3283        return None;
3284    }
3285    let candidates = parent
3286        .named_children(&mut parent.walk())
3287        .filter(|sibling| {
3288            sibling.kind() == "namespace_definition"
3289                && sibling.has_error()
3290                && sibling.end_byte() < anchor.start_byte()
3291        })
3292        .filter_map(|namespace| {
3293            namespace
3294                .child_by_field_name("name")
3295                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3296                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3297        })
3298        .collect::<Vec<_>>();
3299    let [namespace] = candidates.as_slice() else {
3300        return None;
3301    };
3302    Some(namespace.clone())
3303}
3304
3305pub(crate) fn recovered_function_like_export_class_pair_has_body(
3306    node: Node<'_>,
3307    source: &str,
3308    identifier: &str,
3309    range: &Range,
3310) -> bool {
3311    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3312        recovered.name == identifier
3313            && recovered.range.start_byte == range.start_byte
3314            && recovered.range.end_byte == range.end_byte
3315    })
3316}
3317
3318fn recovered_exported_class_has_body(
3319    node: Node<'_>,
3320    source: &str,
3321    expected_name: &str,
3322) -> Option<bool> {
3323    match node.kind() {
3324        "function_definition" => {
3325            let (class_node, name, _, _) =
3326                recover_exported_class_function_definition(node, source)?;
3327            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3328        }
3329        "declaration" | "field_declaration" => {
3330            let recovered = recover_exported_class_declaration(node, source)?;
3331            (recovered.name == expected_name).then(|| recovered.body.is_some())
3332        }
3333        _ => None,
3334    }
3335}
3336
3337/// One file's embedded export-macro class recovery, resolved once and keyed by
3338/// the `ERROR` node that mints each set.
3339///
3340/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3341/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3342/// once per class-like unit in the file. On a translation unit the parser could
3343/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3344/// node spans most of the file -- that is one full subtree pass per class,
3345/// quadratic in the file's size, and it was 78% of that file's inverse scan
3346/// (#1496).
3347///
3348/// Keyed by byte span rather than node identity, so one analyzer generation's
3349/// re-parses of the same content share the index. A nested `ERROR` that shares
3350/// its parent's span recovers the same classes: the only node the parent adds
3351/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3352#[derive(Default)]
3353pub struct CppRecoveredExportClassIndex {
3354    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3355}
3356
3357impl CppRecoveredExportClassIndex {
3358    pub fn build(root: Node<'_>, source: &str) -> Self {
3359        let mut by_error_node: HashMap<
3360            (usize, usize),
3361            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3362        > = HashMap::default();
3363        // One cursor for the whole-file walk, and the error question asked by
3364        // symbol rather than by `kind()` string (#3097).
3365        let mut cursor = root.walk();
3366        let mut stack = vec![root];
3367        while let Some(node) = stack.pop() {
3368            if node.is_error() {
3369                let recovered = recover_embedded_function_like_export_classes(node, source);
3370                if !recovered.is_empty() {
3371                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3372                }
3373            }
3374            stack.extend(node.named_children(&mut cursor));
3375        }
3376        Self { by_error_node }
3377    }
3378
3379    /// The bytes this index holds, for the analyzer cache's weight.
3380    pub fn approximate_size(&self) -> usize {
3381        self.by_error_node
3382            .values()
3383            .fold(0usize, |total, recovered| {
3384                recovered.iter().fold(
3385                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3386                    |acc, class| {
3387                        acc.saturating_add(std::mem::size_of::<
3388                            RecoveredEmbeddedFunctionLikeExportClass,
3389                        >())
3390                        .saturating_add(class.name.len())
3391                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3392                    },
3393                )
3394            })
3395    }
3396
3397    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3398        self.by_error_node
3399            .get(&(node.start_byte(), node.end_byte()))
3400            .is_some_and(|recovered| {
3401                recovered.iter().any(|class| {
3402                    class.name == identifier
3403                        && class.range.start_byte == range.start_byte
3404                        && class.range.end_byte == range.end_byte
3405                })
3406            })
3407    }
3408}
3409
3410// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3411// `recovered_class_body_at` pops while deciding whether a recovered class shape
3412// claims one declaration range. The count is deterministic for a given source,
3413// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3414// pins it directly instead of timing the walk, the way #2358 pinned the
3415// `remove_code_unit` scan.
3416#[cfg(any(test, feature = "test-support"))]
3417thread_local! {
3418    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3419        const { std::cell::Cell::new(0) };
3420}
3421
3422/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3423/// the calling thread since the last
3424/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3425#[cfg(any(test, feature = "test-support"))]
3426#[doc(hidden)]
3427pub fn recovered_class_body_node_visits_for_test() -> usize {
3428    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3429}
3430
3431/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3432#[cfg(any(test, feature = "test-support"))]
3433#[doc(hidden)]
3434pub fn reset_recovered_class_body_node_visits_for_test() {
3435    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3436}
3437
3438#[cfg(any(test, feature = "test-support"))]
3439fn record_recovered_class_body_visit() {
3440    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3441}
3442
3443#[cfg(not(any(test, feature = "test-support")))]
3444fn record_recovered_class_body_visit() {}
3445
3446/// Whether a recovered class shape named `identifier` owns `range`, and if so
3447/// whether that shape has a body.
3448///
3449/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3450/// forward declaration, and `None` means no recovered shape claims the range,
3451/// so the caller reads the plain `class_specifier` family instead. This is the
3452/// single definition of "does a recovered class have a body": the resolver's
3453/// declaration-strength answer and the navigation occurrence role both read it,
3454/// so an export-macro class is a definition on both paths.
3455///
3456/// The walk follows only the nodes whose span covers `range.start_byte`, which
3457/// is every node that can answer. Each recovered shape reports a range that
3458/// starts at the node's own start byte (the function-like export pair, whose
3459/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3460/// class, which the caller gates on an equal start) or at a token inside the
3461/// node (the embedded export class, keyed on its `class` token, and the
3462/// exported class wrapper, gated on containment here), and every one of them is
3463/// accepted only on an exact match with `range`. Descending everywhere instead
3464/// made one declaration-strength question cost a full pass over the file, so
3465/// asking it once per reference was quadratic in file size: 80% of the 385 s
3466/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3467/// (#1496).
3468pub(crate) fn recovered_class_body_at(
3469    recovered_export_classes: &CppRecoveredExportClassIndex,
3470    root: Node<'_>,
3471    source: &str,
3472    identifier: &str,
3473    range: &Range,
3474) -> Option<bool> {
3475    let covers_range_start = |node: &Node<'_>| {
3476        node.start_byte() <= range.start_byte
3477            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3478    };
3479    let mut stack = vec![root];
3480    let mut saw_forward = false;
3481    while let Some(node) = stack.pop() {
3482        record_recovered_class_body_visit();
3483        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3484        // so an unequal start settles it before the recovery reads the node's
3485        // children at all.
3486        if (node.start_byte() == range.start_byte
3487            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3488            || recovered_export_classes.claims(node, identifier, range)
3489            || (node.start_byte() == range.start_byte
3490                && recovered_fragmented_class_has_body(node, source, identifier, range))
3491        {
3492            return Some(true);
3493        }
3494        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3495            return Some(true);
3496        }
3497        // Macro-decorated exported classes are recovered from a malformed
3498        // function_definition/declaration wrapper. Their indexed class range starts at
3499        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3500        // may also extend the indexed range beyond the wrapper through trailing class
3501        // fragments. Match the structured container that owns the range start by its
3502        // recovered name instead of requiring identical boundaries.
3503        if node.start_byte() <= range.start_byte
3504            && range.start_byte < node.end_byte()
3505            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3506        {
3507            if has_body {
3508                return Some(true);
3509            }
3510            saw_forward = true;
3511            continue;
3512        }
3513        let mut cursor = node.walk();
3514        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3515    }
3516    saw_forward.then_some(false)
3517}
3518
3519/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3520/// head of `identifier`'s member list at `range`.
3521///
3522/// An aggregate whose member list opens with a field-list macro invocation
3523/// never forms a specifier, so the ordinary strength read finds no specifier
3524/// node at the indexed range and answers `Unknown`. A complete definition
3525/// recovered this way then loses the defining-type choice to a forward
3526/// declaration of the same tag in another header, and every member the
3527/// definition declares becomes unreachable through it: libuv's
3528/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3529/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3530/// proves the body, so it answers for its own ranges.
3531fn recovered_collapsed_aggregate_has_body(
3532    node: Node<'_>,
3533    source: &str,
3534    identifier: &str,
3535    range: &Range,
3536) -> bool {
3537    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3538        head.key.start_byte() == range.start_byte
3539            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3540    };
3541    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3542        return true;
3543    }
3544    if !node.is_error() {
3545        return false;
3546    }
3547    let mut cursor = node.walk();
3548    let children = node.children(&mut cursor).collect::<Vec<_>>();
3549    children
3550        .iter()
3551        .enumerate()
3552        .filter(|(_, child)| {
3553            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3554        })
3555        .any(|(index, _)| {
3556            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3557        })
3558}
3559
3560/// Whether `node` is the base type displaced into the declarator field of an
3561/// export-macro class that tree-sitter represented as a declaration or
3562/// function definition.
3563///
3564/// Declaration extraction already recovers this exact malformed envelope as a
3565/// class and records the declarator as its base. Reference extraction must use
3566/// the same structural fact instead of treating the node as a function name.
3567pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3568    if !matches!(
3569        node.kind(),
3570        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3571    ) {
3572        return false;
3573    }
3574    if let Some(function) = node.parent().filter(|parent| {
3575        parent.kind() == "function_definition"
3576            && parent
3577                .child_by_field_name("declarator")
3578                .is_some_and(|declarator| same_node(declarator, node))
3579    }) {
3580        return recover_exported_class_function_definition(function, source)
3581            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3582    }
3583    let Some(initializer) = node.parent().filter(|parent| {
3584        parent.kind() == "init_declarator"
3585            && parent
3586                .child_by_field_name("declarator")
3587                .is_some_and(|declarator| same_node(declarator, node))
3588    }) else {
3589        return false;
3590    };
3591    initializer
3592        .parent()
3593        .filter(|parent| parent.kind() == "declaration")
3594        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3595        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3596}
3597
3598/// Recover the class item from a region reparse that still carries the
3599/// sentinel's synthetic function envelope.  An unknown class attribute can
3600/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3601/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3602/// then nested below that function, so direct class-child lookup is not enough.
3603struct CppSentinelReparsedClass<'tree> {
3604    declaration_node: Node<'tree>,
3605    name: String,
3606    body: Node<'tree>,
3607    raw_supertypes: Option<Vec<String>>,
3608    base_facts: Option<Vec<CppBaseSpecifierFact>>,
3609}
3610
3611fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3612    let mut cursor = root.walk();
3613    root.named_children(&mut cursor)
3614        .find(|child| child.kind() != "comment")
3615        .filter(|child| child.kind() == "template_declaration")
3616}
3617
3618fn cpp_sentinel_reparsed_class<'tree>(
3619    root: Node<'tree>,
3620    template_node: Option<Node<'tree>>,
3621    source: &str,
3622    ancestry: &ParentIndex<'tree>,
3623) -> Option<CppSentinelReparsedClass<'tree>> {
3624    let container = template_node.unwrap_or(root);
3625    let mut cursor = container.walk();
3626    for child in container.named_children(&mut cursor) {
3627        if matches!(
3628            child.kind(),
3629            "class_specifier" | "struct_specifier" | "union_specifier"
3630        ) {
3631            let name = class_like_name(child, source, ancestry)?;
3632            let body = cpp_body_node(child)?;
3633            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3634                .then(|| extract_cpp_supertypes(child, source));
3635            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
3636                .then(|| extract_cpp_base_facts(child, source));
3637            return Some(CppSentinelReparsedClass {
3638                declaration_node: child,
3639                name,
3640                body,
3641                raw_supertypes,
3642                base_facts,
3643            });
3644        }
3645        if child.kind() == "declaration"
3646            && let Some(class_node) = first_class_like_child(child)
3647        {
3648            let name = class_like_name(class_node, source, ancestry)?;
3649            let body = cpp_body_node(class_node)?;
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        // Only when the nested class item carries its own body. A bodyless
3664        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3665        // a function definition -- is the export-macro shape recovered by the
3666        // next arm, and must fall through to it rather than abort the search.
3667        if child.kind() == "function_definition"
3668            && let Some(class_node) = first_class_like_child(child)
3669            && let Some(body) = cpp_body_node(class_node)
3670            && let Some(name) = class_like_name(class_node, source, ancestry)
3671        {
3672            let raw_supertypes =
3673                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3674                    .then(|| extract_cpp_supertypes(class_node, source));
3675            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3676                .then(|| extract_cpp_base_facts(class_node, source));
3677            return Some(CppSentinelReparsedClass {
3678                declaration_node: class_node,
3679                name,
3680                body,
3681                raw_supertypes,
3682                base_facts,
3683            });
3684        }
3685        if child.kind() == "function_definition"
3686            && let Some((_, name, raw_supertypes, base_facts)) =
3687                recover_exported_class_function_definition(child, source)
3688        {
3689            let body = cpp_body_node(child)?;
3690            return Some(CppSentinelReparsedClass {
3691                declaration_node: child,
3692                name,
3693                body,
3694                raw_supertypes,
3695                base_facts,
3696            });
3697        }
3698    }
3699    None
3700}
3701
3702fn recovered_postfix_export_macro_base(
3703    node: Node<'_>,
3704    type_node: Node<'_>,
3705    declarator: Node<'_>,
3706    source: &str,
3707) -> Option<String> {
3708    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
3709        .and_then(|base| recovered_malformed_base_name(base, source))
3710}
3711
3712fn recovered_postfix_export_macro_base_node<'tree>(
3713    node: Node<'tree>,
3714    type_node: Node<'tree>,
3715    declarator: Node<'tree>,
3716    source: &str,
3717) -> Option<Node<'tree>> {
3718    let mut cursor = node.walk();
3719    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3720        child.kind() == "ERROR"
3721            && child.start_byte() >= type_node.end_byte()
3722            && child.end_byte() <= declarator.start_byte()
3723            && postfix_export_macro_inheritance(*child, source)
3724    });
3725    malformed_clauses.next()?;
3726    if malformed_clauses.next().is_some() {
3727        return None;
3728    }
3729    Some(declarator)
3730}
3731
3732fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3733    let mut macro_count = 0;
3734    let mut colon_count = 0;
3735    let mut access_count = 0;
3736    for index in 0..node.child_count() {
3737        let Some(child) = node.child(index) else {
3738            return false;
3739        };
3740        match child.kind() {
3741            "identifier" | "type_identifier" if child.is_named() => {
3742                let candidate = normalize_cpp_whitespace(node_text(child, source));
3743                if !cpp_export_macro_token(&candidate) {
3744                    return false;
3745                }
3746                macro_count += 1;
3747            }
3748            ":" if !child.is_named() => colon_count += 1,
3749            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3750            _ => return false,
3751        }
3752    }
3753    macro_count == 1 && colon_count == 1 && access_count == 1
3754}
3755
3756fn recovered_single_base_after_declarator_node<'tree>(
3757    node: Node<'tree>,
3758    declarator: Node<'tree>,
3759    source: &str,
3760) -> Option<Node<'tree>> {
3761    let body_start = node
3762        .child_by_field_name("body")
3763        .map(|body| body.start_byte())
3764        .unwrap_or(node.end_byte());
3765    let mut cursor = node.walk();
3766    let mut bases = node
3767        .named_children(&mut cursor)
3768        .filter(|child| {
3769            child.kind() == "ERROR"
3770                && child.start_byte() >= declarator.end_byte()
3771                && child.end_byte() <= body_start
3772        })
3773        .filter_map(|error| {
3774            displaced_exported_class_name(error, source)?;
3775            let mut error_cursor = error.walk();
3776            error
3777                .named_children(&mut error_cursor)
3778                .find(|child| recovered_malformed_base_name(*child, source).is_some())
3779        });
3780    let base = bases.next()?;
3781    bases.next().is_none().then_some(base)
3782}
3783
3784fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3785    (0..node.child_count()).any(|index| {
3786        node.child(index).is_some_and(|child| {
3787            child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
3788        })
3789    })
3790}
3791
3792fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
3793    matches!(name, "public" | "protected" | "private")
3794}
3795
3796pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3797    recover_exported_class_function_definition(node, source).is_some()
3798}
3799
3800fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3801    matches!(
3802        kind,
3803        "ERROR"
3804            | "preproc_if"
3805            | "preproc_ifdef"
3806            | "preproc_ifndef"
3807            | "preproc_else"
3808            | "preproc_elif"
3809    ) || (kind == "labeled_statement" && in_class_scope)
3810}
3811
3812pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3813    if node.kind() != "declaration" {
3814        return false;
3815    }
3816    let mut ancestor = node.parent();
3817    while let Some(container) = ancestor {
3818        match container.kind() {
3819            "compound_statement" => {
3820                return container.parent().is_some_and(|class_container| {
3821                    is_recovered_exported_class_container(class_container, source)
3822                });
3823            }
3824            // These containers preserve ScopeInfo in visit_node. declaration_list is
3825            // the body container selected for a linkage specification.
3826            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3827            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3828            _ => return false,
3829        }
3830        ancestor = container.parent();
3831    }
3832    false
3833}
3834
3835fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3836    let body_start = node
3837        .child_by_field_name("body")
3838        .map(|body| body.start_byte())
3839        .unwrap_or(node.end_byte());
3840    let mut stack = Vec::new();
3841    for index in (0..node.named_child_count()).rev() {
3842        let Some(child) = node.named_child(index) else {
3843            continue;
3844        };
3845        if child.start_byte() >= body_start {
3846            continue;
3847        }
3848        stack.push(child);
3849    }
3850
3851    let mut best = None;
3852    while let Some(current) = stack.pop() {
3853        if matches!(current.kind(), "identifier" | "type_identifier") {
3854            let name = normalize_cpp_whitespace(node_text(current, source));
3855            if !name.is_empty()
3856                && !cpp_export_macro_token(&name)
3857                && !matches!(name.as_str(), "class" | "struct" | "union")
3858            {
3859                best = Some(name);
3860            }
3861            continue;
3862        }
3863
3864        for index in (0..current.named_child_count()).rev() {
3865            if let Some(child) = current.named_child(index)
3866                && child.start_byte() < body_start
3867            {
3868                stack.push(child);
3869            }
3870        }
3871    }
3872    best
3873}
3874
3875fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3876    if node.kind() == "declaration"
3877        && node
3878            .child_by_field_name("type")
3879            .or_else(|| first_class_like_child(node))
3880            .is_some_and(|type_node| {
3881                matches!(
3882                    type_node.kind(),
3883                    "class_specifier" | "struct_specifier" | "union_specifier"
3884                )
3885            })
3886        && let Some(name) = node
3887            .child_by_field_name("declarator")
3888            .and_then(|declarator| declarator_name_from_node(declarator, source))
3889        && !cpp_export_macro_token(&name)
3890    {
3891        return Some(name);
3892    }
3893
3894    if node.kind() == "function_definition"
3895        && node.child_by_field_name("type").is_some_and(|type_node| {
3896            matches!(
3897                type_node.kind(),
3898                "class_specifier" | "struct_specifier" | "union_specifier"
3899            )
3900        })
3901        && let Some(name) = node
3902            .child_by_field_name("declarator")
3903            .and_then(|declarator| direct_identifier_name(declarator, source))
3904        && !cpp_export_macro_token(&name)
3905    {
3906        return Some(name);
3907    }
3908
3909    let class_node = if matches!(
3910        node.kind(),
3911        "class_specifier" | "struct_specifier" | "union_specifier"
3912    ) {
3913        node
3914    } else {
3915        first_class_like_child(node)?
3916    };
3917    class_like_name_from_children(class_node, source)
3918}
3919
3920/// Normalize the text of a directly selected C++ identifier node.
3921pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3922    if !matches!(
3923        node.kind(),
3924        "identifier" | "field_identifier" | "type_identifier"
3925    ) {
3926        return None;
3927    }
3928    let name = normalize_cpp_whitespace(node_text(node, source));
3929    (!name.is_empty()).then_some(name)
3930}
3931
3932fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3933    if let Some(name) = direct_identifier_name(node, source) {
3934        return Some(name);
3935    }
3936    let mut cursor = node.walk();
3937    node.named_children(&mut cursor)
3938        .find_map(|child| declarator_name_from_node(child, source))
3939}
3940
3941fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3942    let mut cursor = node.walk();
3943    node.named_children(&mut cursor).find(|child| {
3944        matches!(
3945            child.kind(),
3946            "class_specifier" | "struct_specifier" | "union_specifier"
3947        )
3948    })
3949}
3950
3951/// Push a container's children as a `Siblings` cursor rather than snapshotting
3952/// them all with one shared scope: children are visited one at a time so a
3953/// `using namespace X;` sibling can affect the scope threaded to the siblings
3954/// that textually follow it (issue #1093).
3955fn push_cpp_container_work<'tree>(
3956    node: Node<'tree>,
3957    scope: ScopeInfo,
3958    stack: &mut Vec<CppWork<'tree>>,
3959) {
3960    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3961}
3962
3963/// Materialize one selected named-child range with a tree-sitter cursor. The
3964/// cursor advances linearly across the parent's concrete children; repeatedly
3965/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3966fn push_cpp_sibling_range<'tree>(
3967    parent: Node<'tree>,
3968    start_index: usize,
3969    end_index: usize,
3970    scope: ScopeInfo,
3971    stack: &mut Vec<CppWork<'tree>>,
3972) {
3973    let mut cursor = parent.walk();
3974    let children = parent
3975        .named_children(&mut cursor)
3976        .skip(start_index)
3977        .take(end_index.saturating_sub(start_index))
3978        .collect::<Vec<_>>()
3979        .into_iter();
3980    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3981}
3982
3983/// Advance a `Siblings` cursor by one child: dispatch the current child under
3984/// the scope accumulated from its *earlier* siblings, then push a
3985/// continuation for the remaining siblings carrying the scope updated for
3986/// *this* child (only `using namespace X;` directives change it). Pushing the
3987/// continuation before the current child's own node work means the current
3988/// child's subtree fully drains (LIFO) before the next sibling is visited,
3989/// preserving left-to-right order.
3990fn advance_cpp_siblings<'tree>(
3991    mut siblings: CppSiblingsWork<'tree>,
3992    source: &str,
3993    stack: &mut Vec<CppWork<'tree>>,
3994) {
3995    let Some(child) = siblings.children.next() else {
3996        return;
3997    };
3998    let current_scope = siblings.scope.clone();
3999    if let Some(namespace) = cpp_using_namespace_target(child, source) {
4000        siblings.scope.visible_using_namespaces.push(namespace);
4001    }
4002    if !siblings.children.as_slice().is_empty() {
4003        stack.push(CppWork::Siblings(siblings));
4004    }
4005    stack.push(CppWork::Node(CppNodeWork {
4006        node: child,
4007        scope: current_scope,
4008    }));
4009}
4010
4011/// The namespace target of a `using namespace X;` directive, or `None` for
4012/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
4013/// kind. Distinguished structurally by the presence of the grammar's literal
4014/// `namespace` keyword token among the node's children -- not by inspecting
4015/// source text -- so it never misreads a member-importing using-declaration
4016/// as a namespace directive.
4017fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4018    if node.kind() != "using_declaration" {
4019        return None;
4020    }
4021    let mut cursor = node.walk();
4022    let is_namespace_directive = node
4023        .children(&mut cursor)
4024        .any(|child| child.kind() == "namespace");
4025    if !is_namespace_directive {
4026        return None;
4027    }
4028    let target = node.named_child(0)?;
4029    // A leading `::` is the explicit-global marker, not part of the namespace
4030    // path (`using namespace ::std::chrono;`). Drop that AST token before
4031    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4032    // keeps: storing the marker verbatim desynced the legacy package string from
4033    // the FqName bridge, which splits on `::` and drops the empty leading
4034    // component, tripping the package/short boundary assert when a bare-owner
4035    // out-of-line definition borrowed the directive's namespace (#1093 path).
4036    let start = target
4037        .child(0)
4038        .filter(|child| !child.is_named() && child.kind() == "::")
4039        .map_or(target.start_byte(), |marker| marker.end_byte());
4040    let text = normalize_cpp_whitespace(
4041        source
4042            .get(start..target.end_byte())
4043            .expect("using-directive target covers one source range"),
4044    );
4045    (!text.is_empty()).then_some(text)
4046}
4047
4048/// Tokens that open a construct only a matching later token closes.
4049const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4050
4051/// The opening tokens of the constructs a C/C++ parse left open at the end of
4052/// the file, in source order (#3501). Empty unless the tree's root is an ERROR
4053/// node.
4054///
4055/// The grammar has no preprocessor, so a construct opened in one conditional
4056/// branch and closed in another stays open: `#if 0` / `extern "C" {` /
4057/// `#endif` leaves its `{` unmatched until a later `#if 0` / `}` / `#endif`.
4058/// One such pair repairs with a single MISSING token. With more of them, the
4059/// parse version tree-sitter keeps reaches the end of input in its error
4060/// state, and tree-sitter wraps every subtree on that version's stack in one
4061/// ERROR node and returns it as the root (`recover_eof` in its `parser.c`).
4062/// The root's children are then exactly that stack: completed subtrees, and
4063/// the bare tokens of the constructs still waiting to be reduced when input
4064/// ran out. A reduced construct is a node of its own, so a bare opener among
4065/// the root's children is one the parse never closed. tdlib/td's copy of the
4066/// SQLite amalgamation parses this way: its root spans all 8,274,268 bytes,
4067/// and its outermost open construct is the `#ifndef SQLITEINT_H` include guard
4068/// at byte 19,718.
4069pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4070    assert!(
4071        root.parent().is_none(),
4072        "only a tree's root records what its parse left open"
4073    );
4074    if !root.is_error() {
4075        return Vec::new();
4076    }
4077    children_iter(root)
4078        .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4079        .collect()
4080}
4081
4082/// Record every declaration of `parsed` that has a range inside `recovery`
4083/// as recovered, unless a narrower recovery already claimed it, and mark the
4084/// reading partial when any declaration lies there (#3501).
4085///
4086/// `recovery` runs from the outermost construct the parse left open to the
4087/// end of the file. Everything before that construct was completed at file
4088/// scope, as in any parse. Everything after it sat on the parse stack above
4089/// an open construct when input ran out, so the scope and nesting the walk
4090/// gave it are a recovery claim rather than a reading of a completed tree,
4091/// and the file's analysis is partial whichever recovery recorded it.
4092fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4093    let recorded: HashSet<CodeUnit> = parsed
4094        .materialization_records
4095        .iter()
4096        .filter_map(|record| match record {
4097            MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4098            _ => None,
4099        })
4100        .collect();
4101    let mut left_open: Vec<(usize, CodeUnit)> = parsed
4102        .declarations()
4103        .iter()
4104        .filter_map(|unit| {
4105            let ranges = parsed.declaration_ranges(unit);
4106            let first = ranges.iter().map(|range| range.start_byte).min()?;
4107            let last = ranges.iter().map(|range| range.start_byte).max()?;
4108            (last >= recovery.start_byte).then(|| (first, unit.clone()))
4109        })
4110        .collect();
4111    if left_open.is_empty() {
4112        return;
4113    }
4114    parsed.completeness = ParseCompleteness::Partial;
4115    left_open.retain(|(_, unit)| !recorded.contains(unit));
4116    left_open.sort();
4117    for (_, unit) in left_open {
4118        parsed
4119            .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4120    }
4121}
4122
4123/// Extract all requested dialect readings from one primary AST event stream.
4124/// Declaration schedulers retain their own admission, recovery and sibling order;
4125/// structural facts, includes and identifiers are interpreted once for the tree.
4126pub(crate) fn parse_cpp_readings<'tree>(
4127    file: &ProjectFile,
4128    source: &str,
4129    root: Node<'tree>,
4130    ancestry: &ParentIndex<'tree>,
4131    dialects: &[LanguageDialect],
4132) -> Vec<ParsedFile> {
4133    parse_cpp_readings_with_object_macro_fields(
4134        file,
4135        source,
4136        root,
4137        ancestry,
4138        dialects,
4139        HashMap::default(),
4140    )
4141}
4142
4143pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4144    file: &ProjectFile,
4145    source: &str,
4146    root: Node<'tree>,
4147    ancestry: &ParentIndex<'tree>,
4148    dialects: &[LanguageDialect],
4149    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4150) -> Vec<ParsedFile> {
4151    assert!(
4152        dialects.len() == 1
4153            || (dialects.len() == 2
4154                && dialects[0] != LanguageDialect::CppC
4155                && dialects[1] == LanguageDialect::CppC),
4156        "one primary dialect, optionally followed by its C alternative"
4157    );
4158    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4159    let properties = RefCell::new(HashMap::default());
4160    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4161    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4162    let mut using_namespaces = Vec::new();
4163    let mut readings: Vec<_> = dialects
4164        .iter()
4165        .map(|_| ParsedFile::new(String::new()))
4166        .collect();
4167    let mut visitors: Vec<_> = readings
4168        .iter_mut()
4169        .zip(dialects)
4170        .map(|(parsed, dialect)| CppVisitor {
4171            file,
4172            source,
4173            parsed,
4174            source_collector: &collector,
4175            source_properties: &properties,
4176            source_dependencies: &dependencies,
4177            primary_ancestry: ancestry,
4178            primary_root_id: root.id(),
4179            source_links: HashMap::default(),
4180            source_declarations: HashSet::default(),
4181            preprocessor_context_cache: Default::default(),
4182            c_tag_semantics: *dialect == LanguageDialect::CppC,
4183            c_tag_scope_witness: false,
4184            recovered_class_sibling_scopes: HashMap::default(),
4185            consumed_fragment_regions: Vec::new(),
4186            orphaned_namespaces: &orphaned_namespaces,
4187            partitioned_regions: Vec::new(),
4188            namespace_forward_scans: HashMap::default(),
4189            field_owners: None,
4190            recovery_captures: Vec::new(),
4191            active_source_context: None,
4192            object_macro_fields: object_macro_fields.clone(),
4193            ambiguous_object_macro_fields: HashSet::default(),
4194        })
4195        .collect();
4196    let mut declaration_stacks: Vec<_> = dialects
4197        .iter()
4198        .map(|_| {
4199            vec![CppWork::Container(CppContainer {
4200                node: root,
4201                scope: ScopeInfo {
4202                    package_name: String::new(),
4203                    module: None,
4204                    class_unit: None,
4205                    template_signature: None,
4206                    template_metadata: None,
4207                    declarations_are_fields: false,
4208                    recovered_specialization_member_scope: false,
4209                    visible_using_namespaces: Vec::new(),
4210                },
4211            })]
4212        })
4213        .collect();
4214    let kinds = CompiledKinds::compile(
4215        &tree_sitter_cpp::LANGUAGE.into(),
4216        CPP_STRUCTURAL_SPEC.kind_table(),
4217    );
4218    // Forward macro names affect calls before their definitions and require the
4219    // existing bounded preprocessor-only context prepass.
4220    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4221    let mut structural = StructuralFactCollector::new(
4222        &CPP_STRUCTURAL_SPEC,
4223        source,
4224        &context,
4225        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4226        usize::MAX,
4227        None,
4228    );
4229    let mut imports = Vec::new();
4230    let mut includes = Vec::new();
4231    let mut expressions = Vec::new();
4232    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4233    let mut statement_facts = CppStatementFactRows::default();
4234    let mut identifiers = HashSet::default();
4235    let mut entered = HashSet::default();
4236    let mut primary_ordinals = HashMap::default();
4237    let mut stack = vec![(root, None)];
4238    while let Some((node, parent)) = stack.pop() {
4239        assert!(entered.insert(node.id()), "one primary event per AST node");
4240        primary_ordinals.insert(node.id(), primary_ordinals.len());
4241        dependencies.borrow_mut().primary.enter(node, source);
4242        let mut child_parent = parent;
4243        if node.is_named() {
4244            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4245            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4246                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4247            }
4248            if let Some(raw_kind) = kinds.kind_of(&node)
4249                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4250            {
4251                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4252                    node,
4253                    raw_kind,
4254                    parent.map(|id| structural.normalized_kind(id)),
4255                    source,
4256                    &context,
4257                );
4258                let mut source_facts = collector.borrow_mut();
4259                let id = structural
4260                    .enter(node, kind, parent, &mut source_facts)
4261                    .expect("unbounded C++ structural collector");
4262                let mut sink = structural.role_sink(&mut source_facts);
4263                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4264                structural
4265                    .accept_roles(id, sink.into_parts())
4266                    .expect("unbounded C++ structural roles");
4267                child_parent = Some(id);
4268            }
4269            if let Some(fact) = crate::source_facts::capture_expression_fact(
4270                node,
4271                source,
4272                collector.borrow_mut().intern_node(node),
4273                &named_record_sizes,
4274            ) {
4275                expressions.push(fact);
4276            }
4277            if node.kind() == "preproc_include" {
4278                let raw = normalize_cpp_whitespace(node_text(node, source));
4279                if !raw.is_empty() {
4280                    let occurrence = collector.borrow_mut().intern_node(node);
4281                    let target = node.child_by_field_name("path").and_then(|path| {
4282                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4283                        let target = collector.borrow_mut().intern_node(path);
4284                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4285                            declaration: occurrence,
4286                            target,
4287                            path: value.to_owned(),
4288                            quoted,
4289                        });
4290                        Some(target)
4291                    });
4292                    imports.push(SourceImportFact::from_import(
4293                        cpp_include_info(raw),
4294                        occurrence,
4295                        target,
4296                        None,
4297                        Vec::new(),
4298                    ));
4299                }
4300            }
4301            if matches!(
4302                node.kind(),
4303                "type_identifier" | "identifier" | "qualified_identifier"
4304            ) {
4305                let text = node_text(node, source).trim();
4306                if !text.is_empty() {
4307                    identifiers.insert(text.to_owned());
4308                }
4309            }
4310        }
4311        for index in 0..visitors.len() {
4312            // Delay the alternative policy until the primary walk encounters a
4313            // tag whose scope can differ. Its saved declaration stack catches up
4314            // through already-entered events, including recovery, without a
4315            // second AST traversal. A false witness performs no C declaration work.
4316            if index != 0 && !visitors[0].c_tag_scope_witness {
4317                continue;
4318            }
4319            let visitor = &mut visitors[index];
4320            advance_primary_declarations(
4321                visitor,
4322                &mut declaration_stacks[index],
4323                &entered,
4324                ancestry,
4325            );
4326            visitor.drain_partitioned_regions();
4327        }
4328        let mut cursor = node.walk();
4329        let children: Vec<_> = node
4330            .children(&mut cursor)
4331            .map(|child| (child, child_parent))
4332            .collect();
4333        stack.extend(children.into_iter().rev());
4334    }
4335    let reading_count = if visitors[0].c_tag_scope_witness {
4336        visitors.len()
4337    } else {
4338        1
4339    };
4340    visitors.truncate(reading_count);
4341    declaration_stacks.truncate(reading_count);
4342    assert!(
4343        declaration_stacks.iter().all(Vec::is_empty),
4344        "all primary declaration events were consumed"
4345    );
4346    for visitor in &mut visitors {
4347        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4348            .into_iter()
4349            .flat_map(|(unit, declarations)| {
4350                declarations
4351                    .into_iter()
4352                    .map(move |declaration| (declaration, unit.clone()))
4353            })
4354            .collect();
4355    }
4356    let dialect_declarations: Vec<_> = visitors
4357        .iter()
4358        .map(|visitor| visitor.source_declarations.clone())
4359        .collect();
4360    drop(visitors);
4361    readings.truncate(reading_count);
4362    dependencies
4363        .borrow_mut()
4364        .finish_primary(&mut properties.borrow_mut());
4365    // Preserve the existing malformed quoted-directive recovery, but publish its
4366    // explicit source occurrence into the same import family as parsed directives.
4367    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4368    let primary_occurrence_ordinals: HashMap<_, _> = collector
4369        .borrow()
4370        .primary_node_occurrences()
4371        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4372        .collect();
4373    let occurrences = collector.into_inner().finish();
4374    let generic_imports: Vec<_> = (0..imports.len())
4375        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4376        .collect();
4377    let source_facts = ParsedSourceFacts {
4378        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4379            includes,
4380            declarations: {
4381                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4382                facts.sort_by_key(|fact| fact.declaration.get());
4383                facts
4384            },
4385            using_namespaces,
4386            expressions,
4387            statement_facts,
4388        }),
4389        go: None,
4390        java: None,
4391        js_ts: None,
4392        php: None,
4393        scala: None,
4394        ruby: None,
4395        python: None,
4396        source_bytes: source.len(),
4397        occurrences,
4398        structural: structural
4399            .finish(root)
4400            .expect("C++ structural collection finishes"),
4401        native_site_occurrences: Vec::new(),
4402        native_declaration_sources: Vec::new(),
4403        declaration_visibilities: None,
4404        rust_declaration_properties: Vec::new(),
4405        rust_modules: None,
4406        rust_types: Vec::new(),
4407        rust_items: Default::default(),
4408        imports,
4409        generic_imports,
4410        rust_import_contexts: Vec::new(),
4411    };
4412    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4413        parsed.finalize_deferred_replacements();
4414        let links = std::mem::take(&mut parsed.source_declaration_units);
4415        parsed.source_declaration_units = links
4416            .into_iter()
4417            .filter(|(_, unit)| parsed.contains_declaration(unit))
4418            .collect();
4419        parsed.imports = source_facts
4420            .imports
4421            .iter()
4422            .map(|import| import.import_info(&source_facts.occurrences))
4423            .collect();
4424        parsed.type_identifiers = identifiers.clone();
4425        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4426            &source_facts,
4427            &declarations,
4428            &primary_occurrence_ordinals,
4429            &mut parsed.source_declaration_units,
4430            &mut parsed.source_declaration_metadata,
4431        ));
4432    }
4433    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4434        // The recovery runs to the end of the file, which is how a reader of
4435        // the record tells this recovery from a local one (#3501).
4436        let recovery = Range {
4437            start_byte: outermost.start_byte(),
4438            end_byte: source.len(),
4439            start_line: outermost.start_position().row + 1,
4440            end_line: root.end_position().row + 1,
4441        };
4442        for parsed in &mut readings {
4443            record_declarations_left_open(parsed, recovery);
4444        }
4445    }
4446    readings
4447}
4448
4449fn advance_primary_declarations<'tree>(
4450    visitor: &mut CppVisitor<'_, '_>,
4451    stack: &mut Vec<CppWork<'tree>>,
4452    entered: &HashSet<usize>,
4453    ancestry: &ParentIndex<'tree>,
4454) {
4455    while let Some(work) = stack.pop() {
4456        match work {
4457            CppWork::Container(container) => {
4458                push_cpp_container_work(container.node, container.scope, stack)
4459            }
4460            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4461            CppWork::Node(work) => {
4462                if !entered.contains(&work.node.id()) {
4463                    stack.push(CppWork::Node(work));
4464                    return;
4465                }
4466                if !visitor.node_is_inside_consumed_fragment(work.node) {
4467                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4468                }
4469            }
4470        }
4471    }
4472}
4473
4474#[derive(Clone)]
4475struct CppActiveSourceContext {
4476    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4477    namespace: Option<String>,
4478    child_namespace: Option<String>,
4479    outer_lexical_scope: Vec<String>,
4480    child_lexical_scope: Vec<String>,
4481    family: Option<SourceOccurrenceId>,
4482    exhaustive_family: Option<SourceOccurrenceId>,
4483    callable_guard_completion_byte: Option<usize>,
4484}
4485
4486/// One owned container reparse deferred until the primary declaration walk has
4487/// reached its source position. The tree and context are owned so the deferred
4488/// walk never retains a `Node` borrowed from a temporary recovery tree.
4489pub struct CppPartitionedRegion {
4490    tree: Tree,
4491    range: std::ops::Range<usize>,
4492    scope: ScopeInfo,
4493    recovery: Range,
4494    source_context: Option<CppActiveSourceContext>,
4495}
4496
4497pub struct CppVisitor<'a, 'source> {
4498    pub file: &'a ProjectFile,
4499    pub source: &'source str,
4500    pub parsed: &'a mut ParsedFile,
4501    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4502    source_properties: &'a RefCell<
4503        HashMap<
4504            SourceDeclarationId,
4505            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4506        >,
4507    >,
4508    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4509    pub primary_ancestry: &'a ParentIndex<'a>,
4510    pub primary_root_id: usize,
4511    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4512    source_declarations: HashSet<SourceDeclarationId>,
4513    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
4514    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
4515    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
4516    ///
4517    /// C has no nested tag scope: a struct/union/enum tag declared inside
4518    /// another aggregate's member list has the scope of the outer declaration
4519    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
4520    /// therefore declares a file-scope `inner` that a later file-scope
4521    /// `struct inner *p;` legitimately references, where C++ would make the
4522    /// same shape a nested class `outer::inner`. Headers carry no compilation
4523    /// language of their own and keep the conservative C++ interpretation.
4524    pub c_tag_semantics: bool,
4525    /// Sticky evidence that this C++ walk reached a declaration whose scope
4526    /// would change under C tag semantics, including visitor recovery work.
4527    pub(crate) c_tag_scope_witness: bool,
4528    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4529    /// Byte regions whose contents were re-owned by a fragmented export-class
4530    /// recovery (#938): the scattered members between the fragmented
4531    /// declaration and its displaced closing brace are indexed as members of
4532    /// the recovered class by the region reparse, so the ordinary sibling walk
4533    /// must not ALSO index them as top-level declarations (that double-indexing
4534    /// made a scattered nested class ambiguous between `Inner` and
4535    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
4536    /// linear scan at visit time is fine.
4537    pub consumed_fragment_regions: Vec<(usize, usize)>,
4538    /// Lexical namespace corrections and brace boundaries for declarations
4539    /// displaced by parse recovery (issues #1537 and #3087). The parsed
4540    /// ancestors can stop short or extend past their true closes; see
4541    /// [`CppVisitor::recovered_namespace_scope`].
4542    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4543    /// Owned reparses waiting for the outer work loop. Partitioning another
4544    /// swallowed class in a tail must not grow the Rust call stack.
4545    pub partitioned_regions: Vec<CppPartitionedRegion>,
4546    /// The namespace forward declarations already folded out of each tree this
4547    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
4548    /// Empty until the first question, which the overwhelming majority of files
4549    /// never ask.
4550    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
4551    /// Which owners already have field declarations in the parse product, as
4552    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
4553    /// the first enum asks, which most files never do (#2786).
4554    pub field_owners: Option<CppFieldOwnerIndex>,
4555    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
4556    /// happen to the declaration set, innermost last. Empty outside a recovery
4557    /// reparse, which is almost always (#2787).
4558    pub recovery_captures: Vec<CppRecoveryCapture>,
4559    active_source_context: Option<CppActiveSourceContext>,
4560    /// Object-like field-list macros defined earlier in this source. Their
4561    /// replacements are parsed structurally and materialized under each
4562    /// invoking aggregate; no source-text expansion is used.
4563    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4564    /// Names whose active replacement is not a unique structured field list.
4565    /// A later `#undef` resets the ambiguity; another `#define` does not.
4566    pub ambiguous_object_macro_fields: HashSet<String>,
4567}
4568
4569/// One source-order event from an object-like field-list macro environment.
4570///
4571/// The event stream lets an include-closure consumer preserve preprocessor
4572/// invalidation without guessing which parsed header happened to be visited
4573/// first. A conservative consumer may permanently block a name after an
4574/// `undef` when conditional include order is not provable.
4575#[derive(Clone, Debug, PartialEq, Eq)]
4576pub enum ObjectMacroFieldEvent {
4577    Define {
4578        name: String,
4579        replacement: ObjectMacroReplacement,
4580        conditional: bool,
4581    },
4582    Undef {
4583        name: String,
4584        conditional: bool,
4585    },
4586}
4587
4588/// Collect object-like field-list macros from a parsed source in source order.
4589/// Macros with conflicting active replacements are omitted so an include
4590/// closure cannot silently choose one guarded definition over another.
4591pub fn collect_cpp_object_macro_fields<'tree>(
4592    root: Node<'tree>,
4593    source: &str,
4594) -> HashMap<String, ObjectMacroReplacement> {
4595    let mut fields = HashMap::default();
4596    let mut ambiguous = HashSet::default();
4597    for event in collect_cpp_object_macro_field_events(root, source) {
4598        match event {
4599            ObjectMacroFieldEvent::Define {
4600                name,
4601                replacement: value,
4602                conditional,
4603            } => {
4604                if value.is_empty() {
4605                    fields.remove(&name);
4606                    if conditional {
4607                        ambiguous.insert(name);
4608                    } else {
4609                        ambiguous.remove(&name);
4610                    }
4611                } else if ambiguous.contains(&name) {
4612                    // Keep the name blocked until an explicit undef resets it.
4613                } else if let Some(previous) = fields.get(&name) {
4614                    if previous != &value {
4615                        fields.remove(&name);
4616                        ambiguous.insert(name);
4617                    }
4618                } else {
4619                    fields.insert(name, value);
4620                }
4621            }
4622            ObjectMacroFieldEvent::Undef { name, conditional } => {
4623                fields.remove(&name);
4624                if conditional {
4625                    ambiguous.insert(name);
4626                } else {
4627                    ambiguous.remove(&name);
4628                }
4629            }
4630        }
4631    }
4632    fields
4633}
4634
4635/// Collect object-like field-list macro events in source order. Conditional
4636/// branches remain visible in the event stream: callers that cannot prove
4637/// branch selection can invalidate names conservatively instead of inventing
4638/// a replacement from one branch.
4639pub fn collect_cpp_object_macro_field_events<'tree>(
4640    root: Node<'tree>,
4641    source: &str,
4642) -> Vec<ObjectMacroFieldEvent> {
4643    let mut events = Vec::new();
4644    let mut stack = vec![root];
4645    while let Some(node) = stack.pop() {
4646        if node.kind() == "preproc_def"
4647            && let Some(name) = extract_macro_name(node, source)
4648        {
4649            let replacement = object_macro_replacement_of(node, source);
4650            events.push(ObjectMacroFieldEvent::Define {
4651                name,
4652                replacement,
4653                conditional: inside_preprocessor_conditional(node),
4654            });
4655        } else if is_cpp_undef_directive(node, source)
4656            && let Some(argument) = node.child_by_field_name("argument")
4657        {
4658            events.push(ObjectMacroFieldEvent::Undef {
4659                name: node_text(argument, source).trim().to_string(),
4660                conditional: inside_preprocessor_conditional(node),
4661            });
4662        }
4663        let mut cursor = node.walk();
4664        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
4665        stack.extend(children.into_iter().rev());
4666    }
4667    events
4668}
4669
4670fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
4671    let mut current = node.parent();
4672    while let Some(parent) = current {
4673        if is_cpp_preprocessor_conditional(parent) {
4674            return true;
4675        }
4676        current = parent.parent();
4677    }
4678    false
4679}
4680
4681fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
4682    node.kind() == "preproc_call"
4683        && node
4684            .child_by_field_name("directive")
4685            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
4686}
4687
4688impl<'a, 'source> CppVisitor<'a, 'source> {
4689    fn apply_active_source_context(
4690        &self,
4691        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4692    ) {
4693        let Some(context) = self.active_source_context.as_ref() else {
4694            return;
4695        };
4696        fact.guard_requirements = fact
4697            .guard_requirements
4698            .as_ref()
4699            .and_then(crate::source_context::cpp_guard_set_to_runtime)
4700            .zip(context.guards.as_ref())
4701            .and_then(|(local, outer)| {
4702                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4703            })
4704            .as_ref()
4705            .map(crate::source_context::cpp_guard_set_from_runtime);
4706        if fact.callable_guards.is_some() {
4707            fact.callable_guards = fact
4708                .callable_guards
4709                .as_ref()
4710                .and_then(crate::source_context::cpp_guard_set_to_runtime)
4711                .zip(context.guards.as_ref())
4712                .and_then(|(local, outer)| {
4713                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
4714                })
4715                .as_ref()
4716                .map(crate::source_context::cpp_guard_set_from_runtime);
4717        }
4718        fact.callable_guard_completion_byte = fact
4719            .callable_guard_completion_byte
4720            .zip(context.callable_guard_completion_byte)
4721            .map(|(local, outer)| local.max(outer));
4722        fact.conditional_family = fact.conditional_family.or(context.family);
4723        fact.exhaustive_conditional_family = fact
4724            .exhaustive_conditional_family
4725            .or(context.exhaustive_family);
4726        if let Some(alias) = fact.file_scope_alias.as_mut() {
4727            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
4728                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
4729                (Some(outer), None) => Some(outer.clone()),
4730                (None, local) => local,
4731            };
4732        }
4733        if !context.outer_lexical_scope.is_empty() {
4734            let mut lexical = context.outer_lexical_scope.clone();
4735            lexical.append(&mut fact.lexical_path);
4736            fact.lexical_path = lexical;
4737        }
4738    }
4739
4740    fn recovery_ancestry<'tree>(
4741        &self,
4742        root: Node<'tree>,
4743    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
4744        crate::source_dependencies::RecoveryAncestry::new(
4745            root,
4746            self.source,
4747            self.source_dependencies,
4748        )
4749    }
4750
4751    fn capture_source_dependencies(
4752        &self,
4753        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4754        node: Node<'_>,
4755        unit: &CodeUnit,
4756    ) {
4757        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
4758            self.source_dependencies.borrow_mut().register_primary(
4759                fact.declaration,
4760                node,
4761                unit.is_class(),
4762            );
4763        } else {
4764            self.source_dependencies
4765                .borrow()
4766                .apply_recovered(node, unit.is_class(), fact);
4767        }
4768    }
4769
4770    fn link_source_declaration(
4771        &mut self,
4772        ancestry: &ParentIndex<'_>,
4773        unit: &CodeUnit,
4774        node: Node<'_>,
4775    ) {
4776        let (declaration, family) = {
4777            let mut collector = self.source_collector.borrow_mut();
4778            let occurrence = if node.id() == self.primary_root_id
4779                || self.primary_ancestry.contains(node)
4780            {
4781                collector.intern_node(node)
4782            } else {
4783                // A region reparse owns distinct provenance even when its byte range
4784                // equals a primary node or a previously dropped recovery tree.
4785                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
4786            };
4787            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
4788                if self.primary_ancestry.contains(name) {
4789                    collector.intern_node(name)
4790                } else {
4791                    collector
4792                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
4793                }
4794            });
4795            let declaration = collector.declare(occurrence, name);
4796            let family =
4797                crate::graph::resolver::preprocessor_conditional_family_node(node).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            (declaration, family)
4808        };
4809        self.source_declarations.insert(declaration);
4810        if !self.source_properties.borrow().contains_key(&declaration) {
4811            let context = crate::source_context::capture_declaration_context(
4812                node,
4813                unit,
4814                self.source,
4815                &mut self.preprocessor_context_cache,
4816            );
4817            let (exhaustive_family, displaced_namespace_closing_brace) = {
4818                let mut collector = self.source_collector.borrow_mut();
4819                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4820                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4821                    {
4822                        collector.intern_node(family)
4823                    } else {
4824                        collector.intern_embedded(
4825                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4826                        )
4827                    }
4828                });
4829                let displaced_namespace_closing_brace =
4830                    context.displaced_namespace_closing_brace.map(|brace| {
4831                        if brace.id() == self.primary_root_id
4832                            || self.primary_ancestry.contains(brace)
4833                        {
4834                            collector.intern_node(brace)
4835                        } else {
4836                            collector.intern_embedded(
4837                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4838                            )
4839                        }
4840                    });
4841                (exhaustive_family, displaced_namespace_closing_brace)
4842            };
4843            let guard_requirements = context.guard_requirements;
4844            let callable_guards = context.callable_guards;
4845            let callable_activation = context.callable_activation;
4846            let callable_guard_completion_byte = context.callable_guard_completion_byte;
4847            let flattened_macro_namespace = context.flattened_macro_namespace;
4848            self.source_properties
4849                .borrow_mut()
4850                .entry(declaration)
4851                .or_insert_with(|| {
4852                    let mut fact = crate::source_facts::capture_declaration(
4853                        declaration,
4854                        node,
4855                        unit,
4856                        self.source,
4857                        ancestry,
4858                    );
4859                    self.capture_source_dependencies(&mut fact, node, unit);
4860                    fact.occurrence_role =
4861                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
4862                    fact.conditional_family = family;
4863                    fact.guard_requirements = guard_requirements;
4864                    fact.callable_guards = callable_guards;
4865                    fact.callable_activation = callable_activation;
4866                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
4867                    fact.exhaustive_conditional_family = exhaustive_family;
4868                    fact.flattened_macro_namespace = flattened_macro_namespace;
4869                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4870                    self.apply_active_source_context(&mut fact);
4871                    fact
4872                });
4873        }
4874        let links = self.source_links.entry(unit.clone()).or_default();
4875        if !links.contains(&declaration) {
4876            links.push(declaration);
4877        }
4878    }
4879
4880    fn link_source_range(
4881        &mut self,
4882        ancestry: &ParentIndex<'_>,
4883        unit: &CodeUnit,
4884        range: Range,
4885        evidence: Option<Node<'_>>,
4886    ) {
4887        let (declaration, family) = {
4888            let mut collector = self.source_collector.borrow_mut();
4889            let occurrence =
4890                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
4891            let name = evidence
4892                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
4893                .map(|name| {
4894                    if self.primary_ancestry.contains(name) {
4895                        collector.intern_node(name)
4896                    } else {
4897                        collector.intern_embedded(
4898                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
4899                        )
4900                    }
4901                });
4902            let declaration = collector.declare(occurrence, name);
4903            let family = evidence.and_then(|node| {
4904                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
4905                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4906                    {
4907                        collector.intern_node(family)
4908                    } else {
4909                        collector.intern_embedded(
4910                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4911                        )
4912                    }
4913                })
4914            });
4915            (declaration, family)
4916        };
4917        self.source_declarations.insert(declaration);
4918        if let Some(node) = evidence
4919            && !self.source_properties.borrow().contains_key(&declaration)
4920        {
4921            let context = crate::source_context::capture_declaration_context(
4922                node,
4923                unit,
4924                self.source,
4925                &mut self.preprocessor_context_cache,
4926            );
4927            let (exhaustive_family, displaced_namespace_closing_brace) = {
4928                let mut collector = self.source_collector.borrow_mut();
4929                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
4930                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
4931                    {
4932                        collector.intern_node(family)
4933                    } else {
4934                        collector.intern_embedded(
4935                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
4936                        )
4937                    }
4938                });
4939                let displaced_namespace_closing_brace =
4940                    context.displaced_namespace_closing_brace.map(|brace| {
4941                        if brace.id() == self.primary_root_id
4942                            || self.primary_ancestry.contains(brace)
4943                        {
4944                            collector.intern_node(brace)
4945                        } else {
4946                            collector.intern_embedded(
4947                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
4948                            )
4949                        }
4950                    });
4951                (exhaustive_family, displaced_namespace_closing_brace)
4952            };
4953            let guard_requirements = context.guard_requirements;
4954            let callable_guards = context.callable_guards;
4955            let callable_activation = context.callable_activation;
4956            let callable_guard_completion_byte = context.callable_guard_completion_byte;
4957            let flattened_macro_namespace = context.flattened_macro_namespace;
4958            let mut fact = crate::source_facts::capture_declaration(
4959                declaration,
4960                node,
4961                unit,
4962                self.source,
4963                ancestry,
4964            );
4965            self.capture_source_dependencies(&mut fact, node, unit);
4966            fact.occurrence_role =
4967                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
4968            fact.conditional_family = family;
4969            fact.guard_requirements = guard_requirements;
4970            fact.callable_guards = callable_guards;
4971            fact.callable_activation = callable_activation;
4972            fact.callable_guard_completion_byte = callable_guard_completion_byte;
4973            fact.exhaustive_conditional_family = exhaustive_family;
4974            fact.flattened_macro_namespace = flattened_macro_namespace;
4975            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
4976            self.apply_active_source_context(&mut fact);
4977            self.source_properties
4978                .borrow_mut()
4979                .insert(declaration, fact);
4980        }
4981        let links = self.source_links.entry(unit.clone()).or_default();
4982        if !links.contains(&declaration) {
4983            links.push(declaration);
4984        }
4985    }
4986
4987    /// [`Self::link_source_range`] with the name occurrence interned from the
4988    /// subspan that writes it instead of derived from an evidence node.
4989    fn link_source_name_range(&mut self, unit: &CodeUnit, range: Range, name_range: Range) {
4990        let declaration = {
4991            let mut collector = self.source_collector.borrow_mut();
4992            let occurrence =
4993                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
4994            let name =
4995                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
4996            collector.declare(occurrence, Some(name))
4997        };
4998        self.source_declarations.insert(declaration);
4999        let links = self.source_links.entry(unit.clone()).or_default();
5000        if !links.contains(&declaration) {
5001            links.push(declaration);
5002        }
5003    }
5004
5005    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5006        let declaration = *self
5007            .source_links
5008            .get(&unit)
5009            .and_then(|links| links.last())
5010            .expect("signature metadata follows its source declaration");
5011        let metadata_ordinal = self
5012            .parsed
5013            .add_signature_with_metadata(unit.clone(), metadata);
5014        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5015            declaration,
5016            unit,
5017            metadata_ordinal,
5018        };
5019        if !self.parsed.source_declaration_metadata.contains(&link) {
5020            self.parsed.source_declaration_metadata.push(link);
5021        }
5022    }
5023
5024    /// Records `code_unit` with the answers the walk carries forward, then adds
5025    /// it to the parse product.
5026    ///
5027    /// Every declaration this walk publishes goes through this family, so the
5028    /// carried-forward answers see each one exactly once: the field ownership
5029    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
5030    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5031    fn add_declaration(
5032        &mut self,
5033        ancestry: &ParentIndex<'_>,
5034        code_unit: CodeUnit,
5035        node: Node<'_>,
5036        parent: Option<CodeUnit>,
5037        top_level: Option<CodeUnit>,
5038    ) {
5039        self.note_declaration(&code_unit);
5040        self.link_source_declaration(ancestry, &code_unit, node);
5041        let source = self.source;
5042        self.parsed
5043            .add_code_unit(code_unit, node, source, parent, top_level);
5044    }
5045
5046    /// Range-based form of [`Self::add_declaration`].
5047    fn add_declaration_with_range(
5048        &mut self,
5049        ancestry: &ParentIndex<'_>,
5050        code_unit: CodeUnit,
5051        range: Range,
5052        evidence: Option<Node<'_>>,
5053        parent: Option<CodeUnit>,
5054        top_level: Option<CodeUnit>,
5055    ) {
5056        self.note_declaration(&code_unit);
5057        self.link_source_range(ancestry, &code_unit, range, evidence);
5058        self.parsed
5059            .add_code_unit_with_range(code_unit, range, parent, top_level);
5060    }
5061
5062    /// Name-subspan form of [`Self::add_declaration_with_range`]: both ranges
5063    /// come from an index that recorded them, so there is no live evidence
5064    /// node to derive the name token from (#4292).
5065    fn add_declaration_with_name_range(
5066        &mut self,
5067        code_unit: CodeUnit,
5068        range: Range,
5069        name_range: Range,
5070        parent: Option<CodeUnit>,
5071        top_level: Option<CodeUnit>,
5072    ) {
5073        self.note_declaration(&code_unit);
5074        self.link_source_name_range(&code_unit, range, name_range);
5075        self.parsed
5076            .add_code_unit_with_range(code_unit, range, parent, top_level);
5077    }
5078
5079    /// Deferred-replacement form of [`Self::add_declaration`].
5080    fn replace_declaration_deferred(
5081        &mut self,
5082        ancestry: &ParentIndex<'_>,
5083        code_unit: CodeUnit,
5084        node: Node<'_>,
5085        parent: Option<CodeUnit>,
5086        top_level: Option<CodeUnit>,
5087    ) {
5088        self.note_replaced_declaration(&code_unit);
5089        self.link_source_declaration(ancestry, &code_unit, node);
5090        let source = self.source;
5091        self.parsed
5092            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5093    }
5094
5095    /// Range-based form of [`Self::replace_declaration_deferred`].
5096    fn replace_declaration_with_range_deferred(
5097        &mut self,
5098        ancestry: &ParentIndex<'_>,
5099        code_unit: CodeUnit,
5100        range: Range,
5101        evidence: Option<Node<'_>>,
5102        parent: Option<CodeUnit>,
5103        top_level: Option<CodeUnit>,
5104    ) {
5105        self.note_replaced_declaration(&code_unit);
5106        self.link_source_range(ancestry, &code_unit, range, evidence);
5107        self.parsed
5108            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5109    }
5110
5111    /// Notes one declaration about to enter the parse product.
5112    ///
5113    /// A declaration the product already holds is not a creation, so an open
5114    /// recovery capture ignores it -- which is the membership test the set
5115    /// difference it replaces performed. A creation inside a nested recovery
5116    /// belongs to the recoveries around it too, so every open capture takes it.
5117    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5118        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5119            for capture in &mut self.recovery_captures {
5120                if capture.removed_pre_existing.contains(code_unit) {
5121                    continue;
5122                }
5123                if capture.created_units.insert(code_unit.clone()) {
5124                    capture.created.push(code_unit.clone());
5125                }
5126            }
5127        }
5128        if let Some(field_owners) = self.field_owners.as_mut() {
5129            field_owners.record(code_unit, self.file);
5130        }
5131    }
5132
5133    /// Notes one declaration about to replace an existing one.
5134    ///
5135    /// A deferred replacement of a declaration that already owns children
5136    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5137    /// a removal is the one thing the field index cannot absorb by addition.
5138    /// Drop it; the next question rebuilds it from the declarations that
5139    /// survive. A replacement of a unit with no children, and a "replacement"
5140    /// of a unit that is not there at all, remove nothing.
5141    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5142        // Forward and definition occurrences remain real navigation alternatives.
5143        // Only identities removed with replaced children lose their mounts.
5144        for removed in self.declarations_a_replacement_removes(code_unit) {
5145            self.source_links.remove(&removed);
5146        }
5147        let removes_children = self.parsed.contains_declaration(code_unit)
5148            && self
5149                .parsed
5150                .children
5151                .get(code_unit)
5152                .is_some_and(|children| !children.is_empty());
5153        if removes_children {
5154            if !self.recovery_captures.is_empty() {
5155                let removed = self.declarations_a_replacement_removes(code_unit);
5156                for capture in &mut self.recovery_captures {
5157                    for unit in &removed {
5158                        // A declaration this capture watched being created is
5159                        // its own; one it did not is a declaration that was
5160                        // already there when the capture opened, so creating it
5161                        // again is a restoration and not a mint.
5162                        if !capture.created_units.contains(unit) {
5163                            capture.removed_pre_existing.insert(unit.clone());
5164                        }
5165                    }
5166                }
5167            }
5168            self.field_owners = None;
5169        }
5170        self.note_declaration(code_unit);
5171    }
5172
5173    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5174    /// when `code_unit` is replaced: its children, transitively.
5175    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5176        let mut removed = Vec::new();
5177        let mut seen = HashSet::default();
5178        let mut pending: Vec<CodeUnit> = self
5179            .parsed
5180            .children
5181            .get(code_unit)
5182            .cloned()
5183            .unwrap_or_default();
5184        while let Some(unit) = pending.pop() {
5185            if !seen.insert(unit.clone()) {
5186                continue;
5187            }
5188            if let Some(children) = self.parsed.children.get(&unit) {
5189                pending.extend(children.iter().cloned());
5190            }
5191            removed.push(unit);
5192        }
5193        removed
5194    }
5195
5196    fn visit_recovered_function_like_export_class<'tree>(
5197        &mut self,
5198        node: Node<'tree>,
5199        recovered: RecoveredFunctionLikeExportClass,
5200        scope: &ScopeInfo,
5201        stack: &mut Vec<CppWork<'tree>>,
5202        ancestry: &ParentIndex<'tree>,
5203    ) {
5204        let RecoveredFunctionLikeExportClass {
5205            name,
5206            range,
5207            raw_supertypes,
5208            base_facts,
5209            member_outcome,
5210        } = recovered;
5211        let class_unit = self.visit_named_class_like_shape(
5212            node,
5213            name,
5214            None,
5215            true,
5216            Some(range),
5217            raw_supertypes,
5218            base_facts,
5219            scope,
5220            stack,
5221            ancestry,
5222        );
5223        self.parsed
5224            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5225                recovery: range,
5226                unit: class_unit.clone(),
5227            });
5228        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5229            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5230                tree.root_node(),
5231                class_unit.identifier(),
5232                self.source,
5233            )
5234        {
5235            self.visit_recovered_fragment_constructor(
5236                constructor_range,
5237                body,
5238                node,
5239                &class_unit,
5240                scope,
5241                ancestry,
5242            );
5243        }
5244        if let Some(outcome) = member_outcome {
5245            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5246        }
5247    }
5248
5249    fn visit_function_like_export_class_pair<'tree>(
5250        &mut self,
5251        node: Node<'tree>,
5252        scope: &ScopeInfo,
5253        stack: &mut Vec<CppWork<'tree>>,
5254        ancestry: &ParentIndex<'tree>,
5255    ) -> bool {
5256        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5257            return false;
5258        };
5259        let member_outcome = self
5260            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5261        // A malformed class body can escape into several following siblings
5262        // before the next export-macro class head appears. Inspect siblings in
5263        // order and stop at the first envelope that contains such a head. One
5264        // envelope can contain several following classes, all recovered in a
5265        // single bounded traversal.
5266        let mut displaced = node.next_named_sibling();
5267        while let Some(candidate) = displaced {
5268            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5269                break;
5270            }
5271            displaced = candidate.next_named_sibling();
5272        }
5273        // The adjacent initializer_list proves the class body envelope, but
5274        // its children are expression-shaped rather than declaration-shaped.
5275        // Visit only separately recovered members; ordinary callable
5276        // definitions remain available from their out-of-line declarations.
5277        let range = recovered.range;
5278        self.visit_recovered_function_like_export_class(
5279            node,
5280            RecoveredFunctionLikeExportClass {
5281                name: recovered.name,
5282                range,
5283                raw_supertypes: recovered.raw_supertypes,
5284                base_facts: recovered.base_facts,
5285                member_outcome,
5286            },
5287            scope,
5288            stack,
5289            ancestry,
5290        );
5291        self.consumed_fragment_regions
5292            .push((node.start_byte(), range.end_byte));
5293        true
5294    }
5295
5296    fn visit_embedded_function_like_export_classes<'tree>(
5297        &mut self,
5298        node: Node<'tree>,
5299        scope: &ScopeInfo,
5300        stack: &mut Vec<CppWork<'tree>>,
5301        ancestry: &ParentIndex<'tree>,
5302    ) -> bool {
5303        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5304        let found = !recovered_classes.is_empty();
5305        for recovered in recovered_classes {
5306            let member_outcome = self.reparse_fragmented_export_class_members(
5307                &recovered.fragmented_body,
5308                &recovered.name,
5309            );
5310            self.visit_recovered_function_like_export_class(
5311                node,
5312                RecoveredFunctionLikeExportClass {
5313                    name: recovered.name,
5314                    range: recovered.range,
5315                    raw_supertypes: Some(recovered.raw_supertypes),
5316                    base_facts: Some(recovered.base_facts),
5317                    member_outcome,
5318                },
5319                scope,
5320                stack,
5321                ancestry,
5322            );
5323        }
5324        found
5325    }
5326
5327    /// Walk `node`'s container, answering every ancestor question from
5328    /// `ancestry`.
5329    ///
5330    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5331    /// because one tree can be walked more than once -- a header's C and C++
5332    /// readings are the same tree under different tag semantics -- and the
5333    /// parent relation is a property of the tree, not of the reading.
5334    #[allow(clippy::too_many_arguments)]
5335    pub fn visit_container<'tree>(
5336        &mut self,
5337        node: Node<'tree>,
5338        ancestry: &ParentIndex<'tree>,
5339        package_name: &str,
5340        module: Option<CodeUnit>,
5341        class_unit: Option<CodeUnit>,
5342        template_signature: Option<String>,
5343        visible_using_namespaces: Vec<String>,
5344    ) {
5345        let scope = ScopeInfo {
5346            package_name: package_name.to_string(),
5347            module,
5348            class_unit,
5349            template_signature,
5350            template_metadata: None,
5351            declarations_are_fields: false,
5352            recovered_specialization_member_scope: false,
5353            visible_using_namespaces,
5354        };
5355        // The work loop dispatches a container's children, never the container
5356        // itself, so an `ERROR` container that is the seed does not reach the
5357        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5358        // breaks the parse leaves the whole translation unit as that seed
5359        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5360        // macro-error-class recovery reads.
5361        if node.is_error() {
5362            self.visit_object_macro_error_classes(node, &scope);
5363        }
5364        self.run_container_work(node, scope, ancestry);
5365        self.drain_partitioned_regions();
5366    }
5367
5368    /// Drain reparsed containers after their primary walk has indexed them.
5369    /// Recovered nodes retain their own tree and source context while they are
5370    /// visited; only then are their byte ranges marked consumed so a shared
5371    /// primary event stream does not visit the same declarations twice.
5372    fn drain_partitioned_regions(&mut self) {
5373        let mut consumed = Vec::new();
5374        while let Some(CppPartitionedRegion {
5375            tree,
5376            range,
5377            scope,
5378            recovery,
5379            source_context,
5380        }) = self.partitioned_regions.pop()
5381        {
5382            let root = tree.root_node();
5383            let container = root
5384                .descendant_for_byte_range(range.start, range.end)
5385                .expect("the queued container belongs to this tree");
5386            assert_eq!(container.byte_range(), range);
5387            let ancestry = self.recovery_ancestry(root);
5388            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5389                visitor.run_container_work(container, scope, &ancestry)
5390            });
5391            // A recovered walk may enqueue another partition inside this
5392            // region. Delay the outer claim until all nested regions have
5393            // drained, or their nodes would be skipped as already consumed.
5394            consumed.push((range.start, range.end));
5395        }
5396        self.consumed_fragment_regions.extend(consumed);
5397    }
5398
5399    /// Whether a work node lies entirely inside a byte region consumed by a
5400    /// fragmented export-class recovery (#938); such nodes were already indexed
5401    /// as members of the recovered class by the region reparse.
5402    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5403        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5404    }
5405
5406    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5407    /// candidate a recovery is about to reparse but has not yet turned into a
5408    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5409    /// prior structured recovery.
5410    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5411        self.consumed_fragment_regions
5412            .iter()
5413            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5414    }
5415
5416    /// Drive the container work loop from an explicit seed scope to completion. The
5417    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5418    /// stays alive for the whole traversal.
5419    ///
5420    /// Every ancestor question this walk asks is answered from `ancestry`, which
5421    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5422    /// node's position in the tree, which made a generated header with thousands
5423    /// of top-level declarations quadratic (#2361). The index is the caller's
5424    /// because it outlives any one walk: the file's tree is walked twice when a
5425    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5426    /// builds its own index for its own tree.
5427    fn run_container_work<'tree>(
5428        &mut self,
5429        node: Node<'tree>,
5430        scope: ScopeInfo,
5431        ancestry: &ParentIndex<'tree>,
5432    ) {
5433        self.drain_cpp_work(
5434            vec![CppWork::Container(CppContainer { node, scope })],
5435            ancestry,
5436        );
5437    }
5438
5439    /// The work loop itself, from whatever seed the caller built.
5440    ///
5441    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5442    /// that must walk only part of a reparsed tree seeds it with the sibling
5443    /// range it may walk instead, which keeps the `using namespace X;` scope
5444    /// accumulation `advance_cpp_siblings` performs.
5445    fn drain_cpp_work<'tree>(
5446        &mut self,
5447        mut stack: Vec<CppWork<'tree>>,
5448        ancestry: &ParentIndex<'tree>,
5449    ) {
5450        while let Some(work) = stack.pop() {
5451            match work {
5452                CppWork::Container(container) => {
5453                    push_cpp_container_work(container.node, container.scope, &mut stack);
5454                }
5455                CppWork::Siblings(siblings) => {
5456                    advance_cpp_siblings(siblings, self.source, &mut stack);
5457                }
5458                CppWork::Node(work) => {
5459                    if self.node_is_inside_consumed_fragment(work.node) {
5460                        continue;
5461                    }
5462                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5463                }
5464            }
5465        }
5466    }
5467
5468    /// Drain nodes selected by a bounded recovery walk without filtering them
5469    /// against consumed regions from the primary tree.
5470    fn drain_cpp_recovery_work<'tree>(
5471        &mut self,
5472        mut stack: Vec<CppWork<'tree>>,
5473        ancestry: &ParentIndex<'tree>,
5474    ) {
5475        while let Some(work) = stack.pop() {
5476            match work {
5477                CppWork::Container(container) => {
5478                    push_cpp_container_work(container.node, container.scope, &mut stack);
5479                }
5480                CppWork::Siblings(siblings) => {
5481                    advance_cpp_siblings(siblings, self.source, &mut stack);
5482                }
5483                CppWork::Node(work) => {
5484                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5485                }
5486            }
5487        }
5488    }
5489
5490    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5491    /// it only when the entire region is member-shaped. This validation must happen
5492    /// before registering the recovered class because a rejected speculative range
5493    /// must not leak into the ordinary recovery path.
5494    fn reparse_fragmented_export_class_members(
5495        &self,
5496        fragmented: &FragmentedExportBody,
5497        class_name: &str,
5498    ) -> Option<FragmentedExportMembers> {
5499        if fragmented.reparse_start >= fragmented.reparse_end {
5500            return None;
5501        }
5502        let tree = cpp_reparse_fragmented_class_body(
5503            self.source,
5504            fragmented.reparse_start,
5505            fragmented.reparse_end,
5506        )?;
5507        if cpp_reparsed_members_are_indexable_for_class(
5508            tree.root_node(),
5509            self.source,
5510            Some(class_name),
5511        ) {
5512            return Some(FragmentedExportMembers::Complete(tree));
5513        }
5514        let has_conditional_constructor = {
5515            let root = tree.root_node();
5516            let mut cursor = root.walk();
5517            root.named_children(&mut cursor).any(|child| {
5518                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5519            })
5520        };
5521        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5522    }
5523
5524    /// Index an already validated fragmented body as members of `class_unit`. The
5525    /// region reparse keeps each member's exact original byte and line positions.
5526    fn visit_fragmented_export_class_members(
5527        &mut self,
5528        origin: Node<'_>,
5529        outcome: FragmentedExportMembers,
5530        class_unit: CodeUnit,
5531        scope: &ScopeInfo,
5532    ) -> bool {
5533        self.with_recovery_source_context(origin, |visitor| {
5534            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5535        })
5536    }
5537
5538    fn visit_fragmented_export_class_members_in_context(
5539        &mut self,
5540        outcome: FragmentedExportMembers,
5541        class_unit: CodeUnit,
5542        scope: &ScopeInfo,
5543    ) -> bool {
5544        let (tree, complete) = match outcome {
5545            FragmentedExportMembers::Complete(tree) => (tree, true),
5546            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5547        };
5548        let root = tree.root_node();
5549        let class_name = class_unit.identifier().to_string();
5550        let member_scope = ScopeInfo {
5551            // A recovered export-macro class may borrow its namespace from an
5552            // earlier forward declaration even when the malformed node itself
5553            // sits at file scope. Use the recovered class identity as the
5554            // authoritative package for reparsed members as well.
5555            package_name: class_unit.package_name().to_string(),
5556            module: scope.module.clone(),
5557            class_unit: Some(class_unit),
5558            template_signature: scope.template_signature.clone(),
5559            template_metadata: None,
5560            declarations_are_fields: true,
5561            recovered_specialization_member_scope: false,
5562            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5563        };
5564        let reparsed_ancestry = self.recovery_ancestry(root);
5565        if !complete {
5566            // A conditional beginning immediately after an access label can
5567            // fragment one constructor declaration while leaving the rest of
5568            // the class body as unsafe statement soup. Recover only that
5569            // structurally proven constructor and leave the outer-tree
5570            // siblings unconsumed for their ordinary walk.
5571            let mut cursor = root.walk();
5572            let constructors = root
5573                .named_children(&mut cursor)
5574                .filter_map(|child| {
5575                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5576                })
5577                .collect::<Vec<_>>();
5578            // The reparsed region is its own tree, so this drain walks it with
5579            // its own parent index.
5580            for constructor in constructors {
5581                let mut stack = Vec::new();
5582                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
5583                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
5584            }
5585            return false;
5586        }
5587        let mut cursor = root.walk();
5588        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
5589        for member in members.iter().copied().filter(|member| {
5590            cpp_reparsed_defaulted_constructor_is_indexable(
5591                *member,
5592                &members,
5593                Some(class_name.as_str()),
5594                self.source,
5595            )
5596        }) {
5597            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
5598        }
5599        // The reparsed region is its own tree, so this walk indexes it itself.
5600        self.run_container_work(root, member_scope, &reparsed_ancestry);
5601        true
5602    }
5603
5604    fn visit_recovered_fragment_constructor<'tree>(
5605        &mut self,
5606        range: std::ops::Range<usize>,
5607        constructor_body: Node<'tree>,
5608        class_declaration: Node<'tree>,
5609        class_unit: &CodeUnit,
5610        scope: &ScopeInfo,
5611        ancestry: &ParentIndex<'tree>,
5612    ) {
5613        self.with_recovery_source_context(class_declaration, |visitor| {
5614            visitor.visit_recovered_fragment_constructor_in_context(
5615                range,
5616                constructor_body,
5617                class_declaration,
5618                class_unit,
5619                scope,
5620                ancestry,
5621            );
5622        });
5623    }
5624
5625    fn visit_recovered_fragment_constructor_in_context<'tree>(
5626        &mut self,
5627        range: std::ops::Range<usize>,
5628        constructor_body: Node<'tree>,
5629        class_declaration: Node<'tree>,
5630        class_unit: &CodeUnit,
5631        scope: &ScopeInfo,
5632        ancestry: &ParentIndex<'tree>,
5633    ) {
5634        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
5635            return;
5636        };
5637        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
5638            tree.root_node(),
5639            range.start,
5640            class_unit.identifier(),
5641            self.source,
5642        ) else {
5643            return;
5644        };
5645        let member_scope = ScopeInfo {
5646            package_name: class_unit.package_name().to_string(),
5647            module: scope.module.clone(),
5648            class_unit: Some(class_unit.clone()),
5649            template_signature: scope.template_signature.clone(),
5650            template_metadata: None,
5651            declarations_are_fields: true,
5652            recovered_specialization_member_scope: false,
5653            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5654        };
5655        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
5656        else {
5657            return;
5658        };
5659        debug_assert_eq!(function.name, class_unit.identifier());
5660        let code_unit = function.code_unit(self.file.clone());
5661        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
5662        self.add_declaration_with_range(
5663            &recovered_ancestry,
5664            code_unit.clone(),
5665            Range {
5666                start_byte: function_declarator.start_byte(),
5667                end_byte: constructor_body.end_byte(),
5668                start_line: function_declarator.start_position().row + 1,
5669                end_line: constructor_body.end_position().row + 1,
5670            },
5671            Some(function_declarator),
5672            None,
5673            None,
5674        );
5675        let declaration = *self.source_links[&code_unit]
5676            .last()
5677            .expect("recovered constructor source");
5678        {
5679            let mut properties = self.source_properties.borrow_mut();
5680            let fact = properties
5681                .get_mut(&declaration)
5682                .expect("recovered constructor source fact");
5683            fact.occurrence_role =
5684                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
5685            fact.callable_is_constructor = true;
5686        }
5687        self.add_signature_with_metadata(
5688            code_unit.clone(),
5689            cpp_signature_metadata(
5690                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
5691                function_declarator,
5692                cpp_callable_linkage(class_declaration, self.source, ancestry),
5693                self.source,
5694                &recovered_ancestry,
5695            )
5696            .with_declaration_only(false),
5697        );
5698        self.parsed.add_child(class_unit.clone(), code_unit);
5699    }
5700
5701    fn visit_recovered_fragment_prefix_members<'tree>(
5702        &mut self,
5703        origin: Node<'_>,
5704        root: Node<'tree>,
5705        constructor_start: usize,
5706        class_unit: &CodeUnit,
5707        scope: &ScopeInfo,
5708        ancestry: &ParentIndex<'tree>,
5709    ) {
5710        self.with_recovery_source_context(origin, |visitor| {
5711            visitor.visit_recovered_fragment_prefix_members_in_context(
5712                root,
5713                constructor_start,
5714                class_unit,
5715                scope,
5716                ancestry,
5717            );
5718        });
5719    }
5720
5721    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
5722        &mut self,
5723        root: Node<'tree>,
5724        constructor_start: usize,
5725        class_unit: &CodeUnit,
5726        scope: &ScopeInfo,
5727        ancestry: &ParentIndex<'tree>,
5728    ) {
5729        let member_scope = ScopeInfo {
5730            package_name: class_unit.package_name().to_string(),
5731            module: scope.module.clone(),
5732            class_unit: Some(class_unit.clone()),
5733            template_signature: scope.template_signature.clone(),
5734            template_metadata: None,
5735            declarations_are_fields: true,
5736            recovered_specialization_member_scope: false,
5737            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5738        };
5739        let mut root_cursor = root.walk();
5740        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
5741        let mut stack = vec![root];
5742        while let Some(current) = stack.pop() {
5743            if current.kind() == "comment" || current.start_byte() >= constructor_start {
5744                continue;
5745            }
5746            if current
5747                .parent()
5748                .is_some_and(|parent| same_node(parent, root))
5749                && cpp_reparsed_defaulted_constructor_is_indexable(
5750                    current,
5751                    &members,
5752                    Some(class_unit.identifier()),
5753                    self.source,
5754                )
5755            {
5756                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
5757                continue;
5758            }
5759            if current.end_byte() <= constructor_start
5760                && current.kind() != "translation_unit"
5761                && current.kind() != "labeled_statement"
5762                && current.kind() != "ERROR"
5763            {
5764                let mut work_stack = Vec::new();
5765                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
5766                self.drain_cpp_recovery_work(work_stack, ancestry);
5767                continue;
5768            }
5769            if matches!(
5770                current.kind(),
5771                "translation_unit" | "labeled_statement" | "ERROR"
5772            ) {
5773                let mut cursor = current.walk();
5774                stack.extend(current.named_children(&mut cursor));
5775            }
5776        }
5777    }
5778
5779    fn visit_recovered_defaulted_constructor(
5780        &mut self,
5781        statement: Node<'_>,
5782        scope: &ScopeInfo,
5783        ancestry: &ParentIndex<'_>,
5784    ) {
5785        let Some(parent) = scope.class_unit.clone() else {
5786            return;
5787        };
5788        let Some(assignment) = statement.named_child(0) else {
5789            return;
5790        };
5791        let Some(call) = assignment.child_by_field_name("left") else {
5792            return;
5793        };
5794        let Some(function_name) = call.child_by_field_name("function") else {
5795            return;
5796        };
5797        let Some(arguments) = call.child_by_field_name("arguments") else {
5798            return;
5799        };
5800        if function_name.kind() != "identifier"
5801            || node_text(function_name, self.source) != parent.identifier()
5802            || arguments.kind() != "argument_list"
5803            || arguments.named_child_count() != 0
5804        {
5805            return;
5806        }
5807        let function = FunctionInfo {
5808            package_name: scope.package_name.clone(),
5809            owner: Some(CppMemberOwner::Unit(parent.clone())),
5810            name: parent.identifier().to_string(),
5811            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
5812        };
5813        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
5814        if self.parsed.contains_declaration(&code_unit) {
5815            return;
5816        }
5817        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
5818        let metadata = SignatureMetadata::new(
5819            normalize_cpp_whitespace(node_text(statement, self.source)),
5820            Vec::new(),
5821        )
5822        .with_declaration_only(true)
5823        .with_callable_arity(CallableArity::exact(0))
5824        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
5825        // The reparse proves class ownership, but carries no original class
5826        // access ladder. Preserve unknown access rather than guessing it.
5827        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
5828        self.add_signature_with_metadata(code_unit.clone(), metadata);
5829        if let Some(declaration) = self
5830            .source_links
5831            .get(&code_unit)
5832            .and_then(|links| links.last())
5833            .copied()
5834        {
5835            let mut properties = self.source_properties.borrow_mut();
5836            let fact = properties
5837                .get_mut(&declaration)
5838                .expect("recovered defaulted constructor source fact");
5839            fact.occurrence_role =
5840                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
5841            fact.callable_is_constructor = true;
5842        }
5843        self.parsed.add_child(parent, code_unit);
5844    }
5845
5846    /// Capture ancestors only when a proven recovery enters another tree. The
5847    /// ordinary declaration driver never performs this additional ancestor walk.
5848    fn with_recovery_source_context<T>(
5849        &mut self,
5850        node: Node<'_>,
5851        walk: impl FnOnce(&mut Self) -> T,
5852    ) -> T {
5853        use crate::graph::resolver::{
5854            enclosing_namespace_context, merge_preprocessor_guards,
5855            preprocessor_conditional_family_for_declaration_with_cache,
5856            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
5857        };
5858        let previous = self.active_source_context.take();
5859        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
5860        let inherited = (!primary).then_some(previous.as_ref()).flatten();
5861        let local_guards = preprocessor_guard_environment_with_cache(
5862            node,
5863            self.source,
5864            &mut self.preprocessor_context_cache,
5865        );
5866        let guards = match inherited {
5867            Some(outer) => outer
5868                .guards
5869                .as_ref()
5870                .zip(local_guards.as_ref())
5871                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
5872            None => local_guards,
5873        };
5874        let local_namespace = enclosing_namespace_context(node, self.source);
5875        let namespace = match (
5876            inherited.and_then(|outer| outer.child_namespace.as_ref()),
5877            local_namespace,
5878        ) {
5879            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5880            (Some(outer), None) => Some(outer.clone()),
5881            (None, local) => local,
5882        };
5883        let outer_lexical_scope =
5884            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
5885        let mut local_owners = Vec::new();
5886        let mut parent = node.parent();
5887        while let Some(ancestor) = parent {
5888            if matches!(
5889                ancestor.kind(),
5890                "namespace_definition"
5891                    | "class_specifier"
5892                    | "struct_specifier"
5893                    | "union_specifier"
5894                    | "enum_specifier"
5895            ) && let Some(name) = ancestor.child_by_field_name("name")
5896            {
5897                local_owners.push(node_text(name, self.source).to_owned());
5898            }
5899            parent = ancestor.parent();
5900        }
5901        local_owners.reverse();
5902        let mut child_lexical_scope = outer_lexical_scope.clone();
5903        child_lexical_scope.extend(local_owners);
5904        if matches!(
5905            node.kind(),
5906            "namespace_definition"
5907                | "class_specifier"
5908                | "struct_specifier"
5909                | "union_specifier"
5910                | "enum_specifier"
5911        ) && let Some(name) = node.child_by_field_name("name")
5912        {
5913            child_lexical_scope.push(node_text(name, self.source).to_owned());
5914        }
5915        let child_namespace = if node.kind() == "namespace_definition" {
5916            node.child_by_field_name("name")
5917                .map(|name| {
5918                    let local = node_text(name, self.source);
5919                    namespace
5920                        .as_ref()
5921                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
5922                })
5923                .or_else(|| namespace.clone())
5924        } else {
5925            namespace.clone()
5926        };
5927        let mut intern = |family: Node<'_>| {
5928            let mut collector = self.source_collector.borrow_mut();
5929            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5930                collector.intern_node(family)
5931            } else {
5932                collector
5933                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
5934            }
5935        };
5936        let family = preprocessor_conditional_family_node(node)
5937            .map(&mut intern)
5938            .or_else(|| inherited.and_then(|outer| outer.family));
5939        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
5940            node,
5941            &mut self.preprocessor_context_cache,
5942        )
5943        .map(&mut intern)
5944        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
5945        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
5946            node,
5947            self.source,
5948            &mut self.preprocessor_context_cache,
5949        );
5950        let callable_guard_completion_byte = match inherited {
5951            Some(outer) => local_completion
5952                .zip(outer.callable_guard_completion_byte)
5953                .map(|(local, outer)| local.max(outer)),
5954            None => local_completion,
5955        };
5956        self.active_source_context = Some(CppActiveSourceContext {
5957            guards,
5958            namespace: child_namespace.clone(),
5959            child_namespace,
5960            outer_lexical_scope: child_lexical_scope.clone(),
5961            child_lexical_scope,
5962            family,
5963            exhaustive_family,
5964            callable_guard_completion_byte,
5965        });
5966        let result = walk(self);
5967        self.active_source_context = previous;
5968        result
5969    }
5970
5971    fn visit_node<'tree>(
5972        &mut self,
5973        node: Node<'tree>,
5974        scope: &ScopeInfo,
5975        stack: &mut Vec<CppWork<'tree>>,
5976        ancestry: &ParentIndex<'tree>,
5977    ) {
5978        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
5979            self.visit_node(node, &recovered_scope, stack, ancestry);
5980            return;
5981        }
5982        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
5983            self.visit_node(node, &recovered_scope, stack, ancestry);
5984            return;
5985        }
5986        // Fragmented-class recovery below may consume a malformed function
5987        // envelope before the ordinary kind dispatch runs. Recover any
5988        // export-macro class embedded in that envelope first; the strict class
5989        // head/base/body predicate is independent of which later recovery owns
5990        // the surrounding parser fragment.
5991        if node.kind() == "function_definition" && node.has_error() {
5992            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
5993        }
5994        if let Some(FragmentedClassRecovery {
5995            declaration_node: class_node,
5996            name,
5997            raw_supertypes,
5998            body: fragmented,
5999        }) = fragmented_class_body(node, self.source)
6000        {
6001            let displaced_namespace_items =
6002                displaced_fragment_namespace_geometry(node, self.source)
6003                    .map(|boundary| boundary.namespace_items)
6004                    .unwrap_or_default();
6005            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6006            let mut class_stack = Vec::new();
6007            // When the full body cannot be safely reparsed, the original class
6008            // node still proves ownership for its parser-visible prefix.
6009            let parser_visible_body =
6010                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6011                    .then(|| cpp_body_node(class_node))
6012                    .flatten();
6013            let class_unit = self.visit_named_class_like_shape(
6014                class_node,
6015                name,
6016                parser_visible_body,
6017                true,
6018                Some(fragmented.class_range),
6019                Some(raw_supertypes),
6020                Some(extract_cpp_base_facts(class_node, self.source)),
6021                scope,
6022                &mut class_stack,
6023                ancestry,
6024            );
6025            let member_scope = ScopeInfo {
6026                package_name: class_unit.package_name().to_string(),
6027                module: scope.module.clone(),
6028                class_unit: Some(class_unit.clone()),
6029                template_signature: scope.template_signature.clone(),
6030                template_metadata: None,
6031                declarations_are_fields: true,
6032                recovered_specialization_member_scope: false,
6033                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6034            };
6035            let complete = outcome.is_some_and(|outcome| {
6036                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6037            });
6038            if complete {
6039                self.consumed_fragment_regions
6040                    .push((node.start_byte(), fragmented.class_range.end_byte));
6041            } else {
6042                // A macro-constrained member can make the full body reparse
6043                // unsafe while tree-sitter still exposes later class members
6044                // as bounded siblings up to the displaced `}`/`;`. Keep the
6045                // structurally proven class/base declaration and re-own those
6046                // sibling nodes under it. They retain their original parser
6047                // nodes and exact ranges; the close boundary comes solely from
6048                // `fragmented_class_body`.
6049                // Template wrappers put the escaped members beside the
6050                // template rather than beside its malformed declaration.
6051                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6052                    if candidate.start_byte() >= fragmented.reparse_end {
6053                        break;
6054                    }
6055                    if cpp_fragment_sibling_is_class_member(
6056                        candidate,
6057                        fragmented.reparse_end,
6058                        self.source,
6059                    ) {
6060                        self.recovered_class_sibling_scopes
6061                            .insert(candidate.id(), member_scope.clone());
6062                    }
6063                }
6064            }
6065            for item in displaced_namespace_items {
6066                self.recovered_class_sibling_scopes
6067                    .insert(item.id(), scope.clone());
6068            }
6069            stack.extend(class_stack);
6070            return;
6071        }
6072        if self.visit_folded_aggregate(node, scope) {
6073            return;
6074        }
6075        match node.kind() {
6076            "template_declaration" => {
6077                if let Some(recovered) =
6078                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6079                {
6080                    let mut template_scope = scope.clone();
6081                    template_scope.template_signature =
6082                        cpp_template_signature(node, recovered.declaration_node, self.source);
6083                    template_scope.template_metadata =
6084                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6085                    let raw_supertypes =
6086                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6087                    let mut class_stack = Vec::new();
6088                    let class_unit = self.visit_named_class_like_shape(
6089                        recovered.class_node,
6090                        recovered.name,
6091                        Some(recovered.body),
6092                        true,
6093                        Some(recovered.range),
6094                        raw_supertypes,
6095                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6096                        &template_scope,
6097                        &mut class_stack,
6098                        ancestry,
6099                    );
6100                    self.parsed.record_materialization(
6101                        MaterializationRecord::RecoveredDeclaration {
6102                            recovery: recovered.range,
6103                            unit: class_unit.clone(),
6104                        },
6105                    );
6106                    let member_scope = ScopeInfo {
6107                        package_name: template_scope.package_name.clone(),
6108                        module: template_scope.module.clone(),
6109                        class_unit: Some(class_unit.clone()),
6110                        template_signature: template_scope.template_signature.clone(),
6111                        template_metadata: None,
6112                        declarations_are_fields: true,
6113                        recovered_specialization_member_scope: recovered
6114                            .class_node
6115                            .child_by_field_name("name")
6116                            .is_some_and(|name| name.kind() == "template_type"),
6117                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6118                    };
6119                    for tail_member in recovered.tail_members.into_iter().rev() {
6120                        stack.push(CppWork::Node(CppNodeWork {
6121                            node: tail_member,
6122                            scope: member_scope.clone(),
6123                        }));
6124                    }
6125                    stack.extend(class_stack);
6126                    for sibling in recovered.member_siblings {
6127                        self.recovered_class_sibling_scopes
6128                            .insert(sibling.id(), member_scope.clone());
6129                    }
6130                    return;
6131                }
6132                for index in (0..node.named_child_count()).rev() {
6133                    let Some(child) = node.named_child(index) else {
6134                        continue;
6135                    };
6136                    if matches!(
6137                        child.kind(),
6138                        "class_specifier"
6139                            | "struct_specifier"
6140                            | "union_specifier"
6141                            | "enum_specifier"
6142                            | "function_definition"
6143                            | "declaration"
6144                            | "field_declaration"
6145                            | "alias_declaration"
6146                            | "namespace_definition"
6147                    ) {
6148                        let mut template_scope = scope.clone();
6149                        template_scope.template_signature =
6150                            cpp_template_signature(node, child, self.source);
6151                        template_scope.template_metadata =
6152                            cpp_template_metadata(node, child, self.source, ancestry);
6153                        if let Some(recovered) = recover_fragmented_partial_specialization(
6154                            node,
6155                            child,
6156                            self.source,
6157                            ancestry,
6158                        ) {
6159                            let code_unit = self.visit_named_class_like_shape(
6160                                recovered.declaration_node,
6161                                recovered.name,
6162                                None,
6163                                true,
6164                                Some(recovered.range),
6165                                None,
6166                                None,
6167                                &template_scope,
6168                                stack,
6169                                ancestry,
6170                            );
6171                            self.parsed.record_materialization(
6172                                MaterializationRecord::RecoveredDeclaration {
6173                                    recovery: recovered.range,
6174                                    unit: code_unit.clone(),
6175                                },
6176                            );
6177                            let mut member_scope = template_scope.clone();
6178                            member_scope.class_unit = Some(code_unit);
6179                            member_scope.declarations_are_fields = true;
6180                            member_scope.recovered_specialization_member_scope = true;
6181                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6182                                stack.push(CppWork::Node(CppNodeWork {
6183                                    node: prefix_member,
6184                                    scope: member_scope.clone(),
6185                                }));
6186                            }
6187                            for sibling in recovered.member_siblings {
6188                                self.recovered_class_sibling_scopes
6189                                    .insert(sibling.id(), member_scope.clone());
6190                            }
6191                            for following in recovered.following_declarations.into_iter().rev() {
6192                                stack.push(CppWork::Node(CppNodeWork {
6193                                    node: following,
6194                                    scope: scope.clone(),
6195                                }));
6196                            }
6197                            return;
6198                        }
6199                        stack.push(CppWork::Node(CppNodeWork {
6200                            node: child,
6201                            scope: template_scope,
6202                        }));
6203                    }
6204                }
6205            }
6206            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6207            "linkage_specification" => {
6208                if let Some(body) = cpp_body_node(node) {
6209                    // A linkage specification either wraps a direct declaration or a
6210                    // braced declaration_list. Visit the direct item itself so
6211                    // declaration recoveries still run on its original syntax node.
6212                    let work = if body.kind() == "declaration_list" {
6213                        CppWork::Container(CppContainer {
6214                            node: body,
6215                            scope: scope.clone(),
6216                        })
6217                    } else {
6218                        CppWork::Node(CppNodeWork {
6219                            node: body,
6220                            scope: scope.clone(),
6221                        })
6222                    };
6223                    stack.push(work);
6224                } else {
6225                    stack.push(CppWork::Container(CppContainer {
6226                        node,
6227                        scope: scope.clone(),
6228                    }));
6229                }
6230            }
6231            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6232                self.visit_class_like(node, scope, stack, ancestry)
6233            }
6234            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6235            // A bare namespace-begin sentinel can make tree-sitter promote the
6236            // wrapped declaration to an ERROR node instead of the usual bogus
6237            // function_definition envelope. Keep the recovery entry point on
6238            // the same structured path for both shapes; ordinary ERROR nodes
6239            // retain their declaration-preserving wrapper traversal when the
6240            // sentinel predicate does not match.
6241            "ERROR" => {
6242                self.visit_object_macro_error_classes(node, scope);
6243                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6244                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6245                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6246                        return;
6247                    }
6248                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6249                        return;
6250                    }
6251                    self.visit_macro_swallowed_function_declarations(node, scope);
6252                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6253                    self.visit_stranded_class_members(node, scope, ancestry);
6254                    stack.push(CppWork::Container(CppContainer {
6255                        node,
6256                        scope: scope.clone(),
6257                    }));
6258                }
6259            }
6260            "declaration" => {
6261                if node.has_error() {
6262                    // Object-like macro lines before a function-like
6263                    // export-macro class head keep the head in a `declaration`
6264                    // rather than in an `ERROR` (#2924). It is the same head
6265                    // and the same following-sibling body, so it takes the same
6266                    // recovery; without it the head's macro invocation indexes
6267                    // as a field named after the macro.
6268                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6269                        return;
6270                    }
6271                    self.visit_prototype_macro_declarations(node, scope);
6272                    if self.node_is_inside_consumed_fragment(node) {
6273                        // The whole declaration was a recovered K&R
6274                        // prototype macro invocation (issue #2932); the
6275                        // reparse above already indexed its Function.
6276                        // Falling through to the ordinary declaration
6277                        // visitor would additionally mint the swapped-field
6278                        // wreckage this recovery exists to replace.
6279                        return;
6280                    }
6281                }
6282                if scope.class_unit.is_some()
6283                    && scope.declarations_are_fields
6284                    && scope.recovered_specialization_member_scope
6285                    && let Some(alias_name) =
6286                        recovered_using_declaration_alias_name(node, self.source)
6287                {
6288                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6289                } else {
6290                    self.visit_declaration(
6291                        node,
6292                        scope,
6293                        scope.declarations_are_fields,
6294                        stack,
6295                        ancestry,
6296                    )
6297                }
6298            }
6299            // A K&R prototype macro invocation with a pointer return type and a
6300            // simple argument list parses with no ERROR node at all: tree-sitter
6301            // reads `T *name _(( args ));` as a multiplication of a type by a
6302            // call (`T * name_(...)`), wrapped in an `expression_statement`
6303            // that `has_error()` only because of a `MISSING "::"` inside it
6304            // (issue #2932). Nothing else mints a declaration from an
6305            // `expression_statement` at declaration scope today, so there is
6306            // no existing behavior to preserve here if this node is not the
6307            // K&R shape; the admission gate inside
6308            // `visit_prototype_macro_declarations` (exactly one clean function
6309            // prototype spanning the recovered run) is what keeps this arm
6310            // safe on an ordinary errorful expression statement that happens
6311            // to contain nested parentheses.
6312            "expression_statement" => {
6313                if node.has_error() {
6314                    self.visit_prototype_macro_declarations(node, scope);
6315                }
6316            }
6317            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
6318            "preproc_call" => self.visit_preproc_call(node, scope),
6319            "type_definition" | "alias_declaration" => {
6320                self.visit_type_declaration(node, scope, stack, ancestry)
6321            }
6322            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6323            // `#include` is collected by the coordinated primary events before the
6324            // walk, so a directive the container walk never reaches -- inside
6325            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6326            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6327            // include claim.
6328            "preproc_include" => {}
6329            // The grammar represents a split extern-C condition as sibling
6330            // markers; declarations between them remain unconditional file
6331            // scope items and need no special declaration metadata.
6332            "preproc_linkage_open" | "preproc_linkage_close" => {}
6333            kind if preserves_declaration_scope_through_wrapper(
6334                kind,
6335                scope.class_unit.is_some(),
6336            ) =>
6337            {
6338                // A preprocessor conditional gates every declaration inside it
6339                // on a configuration this analyzer never evaluates; record the
6340                // interval so declaration state can say so (issue #1476). The
6341                // else/elif branches are children of the `preproc_if` node, so
6342                // recording the openers covers every branch.
6343                if kind == "labeled_statement" {
6344                    self.visit_access_label_constructor(node, scope);
6345                }
6346                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6347                    let mut range = cpp_declaration_range(node);
6348                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6349                        range.end_byte = boundary.end_byte;
6350                        range.end_line = boundary.end_line;
6351                    }
6352                    self.parsed.record_materialization(
6353                        MaterializationRecord::ConfigurationConditional { range },
6354                    );
6355                    if node.has_error() {
6356                        // A malformed export-macro class can close the namespace
6357                        // node early while the enclosing include guard still owns
6358                        // the remaining class-head/body pairs. The ordinary walk
6359                        // cannot carry the lost namespace through those promoted
6360                        // siblings. Scan only structured ERROR nodes in this
6361                        // already-malformed conditional; the pair recovery's
6362                        // exact class/macro/body predicate remains the admission
6363                        // gate, and its namespace lifting restores the owner.
6364                        let mut candidates = vec![node];
6365                        while let Some(candidate) = candidates.pop() {
6366                            // An `ERROR` still inside a namespace body is one
6367                            // the ordinary walk reaches with that namespace in
6368                            // scope. Claiming it here registers the recovered
6369                            // class at file scope and the consumed region then
6370                            // suppresses the walk that would have named it
6371                            // correctly -- which is why Botan's `DL_Group` was
6372                            // `DL_Group` and not `Botan.DL_Group` in the real
6373                            // header, where an include guard wraps the
6374                            // namespace, and was right in a fixture without one
6375                            // (#2552).
6376                            if candidate.kind() == "ERROR"
6377                                && !cpp_is_inside_namespace_body(candidate, ancestry)
6378                                && self.visit_function_like_export_class_pair(
6379                                    candidate, scope, stack, ancestry,
6380                                )
6381                            {
6382                                continue;
6383                            }
6384                            for index in (0..candidate.named_child_count()).rev() {
6385                                candidates.push(
6386                                    candidate
6387                                        .named_child(index)
6388                                        .expect("index below the node's own named child count"),
6389                                );
6390                            }
6391                        }
6392                    }
6393                }
6394                stack.push(CppWork::Container(CppContainer {
6395                    node,
6396                    scope: scope.clone(),
6397                }))
6398            }
6399            // A macro invocation the parser could not group leaves its head in
6400            // whatever slot was free. The `function_definition` and `ERROR`
6401            // arms above try the same recovery on the envelopes they get; a
6402            // flattened run leaves a bare `identifier`/`type_identifier`, and a
6403            // swallowed tail can land in a `parameter_declaration` (#3094).
6404            _ => {
6405                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6406            }
6407        }
6408    }
6409
6410    fn visit_macro_swallowed_function_declarations<'tree>(
6411        &mut self,
6412        envelope: Node<'tree>,
6413        scope: &ScopeInfo,
6414    ) {
6415        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
6416            || envelope.kind() == "ERROR"
6417                && envelope
6418                    .parent()
6419                    .is_some_and(|parent| parent.kind() == "ERROR")
6420        {
6421            return;
6422        }
6423        let mut stack = (0..envelope.named_child_count())
6424            .filter_map(|index| envelope.named_child(index))
6425            .collect::<Vec<_>>();
6426        while let Some(node) = stack.pop() {
6427            if node.kind() == "function_declarator" {
6428                self.visit_error_swallowed_function_declaration(node, scope);
6429            }
6430            for child in named_children_iter(node) {
6431                stack.push(child);
6432            }
6433        }
6434    }
6435
6436    /// Index the declarations an attribute-like macro invocation swallowed into
6437    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
6438    /// the shape and why the parser produces it.
6439    fn visit_macro_wrapped_declarations<'tree>(
6440        &mut self,
6441        envelope: Node<'tree>,
6442        scope: &ScopeInfo,
6443        ancestry: &ParentIndex<'tree>,
6444    ) {
6445        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
6446        if recovered.is_empty() {
6447            return;
6448        }
6449        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
6450        self.record_recovered_declarations(recovery, envelope, |visitor| {
6451            for declaration in recovered {
6452                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
6453            }
6454        });
6455    }
6456
6457    /// Index the declarations a macro invocation collapsed. See
6458    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
6459    /// produces it. Returns whether it claimed the region beginning at
6460    /// `envelope`.
6461    ///
6462    /// The parser's own nodes cannot be read the way the swallowed tail of the
6463    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
6464    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
6465    /// with no declarator left at all, and the declarators that do survive on
6466    /// the envelope's declarator spine pair one declaration's name with the
6467    /// *next* declaration's parameter list. Reading those would be a guess.
6468    ///
6469    /// The bytes are still ordinary declarations, though, and the collapse is
6470    /// the parser carrying the failure forward from one macro invocation. So
6471    /// reparse the region, walk the items the parser makes of it, and when one
6472    /// item is itself a collapsed run, recover that invocation from its own
6473    /// bytes and resume the scan just past it. Each pass starts later than the
6474    /// last, so the scan is a loop over the invocations that collapse, not over
6475    /// the declarations: `llama.h` needs three passes for 214 declarations.
6476    ///
6477    /// The region can reach past `envelope` into the siblings the parser handed
6478    /// the rest of one invocation, so it is recorded as consumed: those
6479    /// siblings are this recovery's, and the ordinary walk must not read them
6480    /// again. Recording it after the scan, not before, keeps the scan's own
6481    /// reparsed nodes -- which carry their original byte offsets -- visible to
6482    /// the walk it drives.
6483    fn visit_collapsed_macro_declaration_run<'tree>(
6484        &mut self,
6485        envelope: Node<'tree>,
6486        scope: &ScopeInfo,
6487        ancestry: &ParentIndex<'tree>,
6488    ) -> bool {
6489        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
6490            return false;
6491        };
6492        let start = envelope.start_byte();
6493        let end = run.region_end;
6494        let recovery = cpp_recovery_window(self.source, start, end);
6495        self.record_recovered_declarations(recovery, envelope, |visitor| {
6496            let mut position = start;
6497            while position < end {
6498                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
6499                    return;
6500                };
6501                let root = tree.root_node();
6502                // A region reparse is its own tree and needs its own parent
6503                // index and dependency arena; the caller's answers nothing
6504                // about these nodes.
6505                let ancestry = visitor.recovery_ancestry(root);
6506                let mut cursor = root.walk();
6507                let collapsed =
6508                    root.named_children(&mut cursor)
6509                        .enumerate()
6510                        .find_map(|(index, item)| {
6511                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
6512                                .map(|run| (index, item, run))
6513                        });
6514                // Walk only the items before the collapsed one. It swallowed
6515                // the rest of the region, so handing it to the ordinary walk
6516                // would re-enter this recovery on a region that starts where
6517                // this one did.
6518                let mut stack = Vec::new();
6519                push_cpp_sibling_range(
6520                    root,
6521                    0,
6522                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
6523                    scope.clone(),
6524                    &mut stack,
6525                );
6526                visitor.drain_cpp_work(stack, &ancestry);
6527                let Some((_, item, run)) = collapsed else {
6528                    return;
6529                };
6530                // On its own bytes the invocation is the declaration-scope
6531                // shape `macro_wrapped_declarations` reads, so its wrapped
6532                // declaration needs no reader of its own here.
6533                if let Some(head) =
6534                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
6535                {
6536                    let head_root = head.root_node();
6537                    let head_ancestry = visitor.recovery_ancestry(head_root);
6538                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
6539                }
6540                assert!(
6541                    run.invocation_end > position,
6542                    "a collapsed run at {position} must end after the byte the scan resumed \
6543                     from, but ended at {}",
6544                    run.invocation_end
6545                );
6546                position = run.invocation_end;
6547            }
6548        });
6549        self.consumed_fragment_regions.push((start, end));
6550        true
6551    }
6552
6553    /// Index the members a string-argument attribute macro stranded in an
6554    /// `ERROR` inside a class body.
6555    ///
6556    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
6557    /// grouping of the attributed member and of every member written after it
6558    /// until it recovers. The members are still whole `function_declarator`
6559    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
6560    /// export-macro class such as Botan's `DL_Group` was indexed with no
6561    /// members at all (#2552).
6562    fn visit_stranded_class_members<'tree>(
6563        &mut self,
6564        node: Node<'tree>,
6565        scope: &ScopeInfo,
6566        ancestry: &ParentIndex<'tree>,
6567    ) {
6568        if scope.class_unit.is_none() || !scope.declarations_are_fields {
6569            return;
6570        }
6571        for member in stranded_declaration_run(node, self.source).declarations {
6572            self.add_macro_wrapped_declaration(member, scope, ancestry);
6573        }
6574    }
6575
6576    /// Index the constructor an access label swallowed in a reparsed class
6577    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
6578    ///
6579    /// The declarator is recovered by reparsing from that call to the end of
6580    /// the label's own statement, which is the same offset-preserving region
6581    /// reparse every other recovery here uses, so the recovered nodes carry
6582    /// their true source positions.
6583    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6584        let Some(class_unit) = scope.class_unit.clone() else {
6585            return;
6586        };
6587        if !scope.declarations_are_fields {
6588            return;
6589        }
6590        let class_name = class_unit.identifier().to_string();
6591        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
6592        else {
6593            return;
6594        };
6595        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
6596            return;
6597        };
6598        let root = tree.root_node();
6599        let Some(declarator) =
6600            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
6601        else {
6602            return;
6603        };
6604        let reparsed_ancestry = self.recovery_ancestry(root);
6605        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
6606        let range = cpp_declaration_range(definition.unwrap_or(declarator));
6607        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
6608        self.record_recovered_declarations(recovery, node, |visitor| {
6609            visitor.add_macro_wrapped_declaration(
6610                MacroWrappedDeclaration {
6611                    declarator,
6612                    range,
6613                    is_static: false,
6614                },
6615                scope,
6616                &reparsed_ancestry,
6617            );
6618        });
6619    }
6620
6621    fn add_macro_wrapped_declaration<'tree>(
6622        &mut self,
6623        declaration: MacroWrappedDeclaration<'tree>,
6624        scope: &ScopeInfo,
6625        ancestry: &ParentIndex<'tree>,
6626    ) {
6627        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
6628        else {
6629            return;
6630        };
6631        let code_unit =
6632            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
6633        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
6634        let is_constructor = scope
6635            .class_unit
6636            .as_ref()
6637            .is_some_and(|class| function.name == class.identifier())
6638            || cpp_callable_is_structural_constructor(
6639                declaration.declarator,
6640                self.source,
6641                ancestry,
6642            );
6643        let already_declared = self.parsed.contains_declaration(&code_unit);
6644        if already_declared {
6645            self.parsed
6646                .record_navigation_range(code_unit.clone(), declaration.range);
6647            if definition.is_none() {
6648                return;
6649            }
6650            // Preserve the earlier declaration-only occurrence while adding
6651            // the recovered body as a distinct definition occurrence.
6652        }
6653        self.add_declaration_with_range(
6654            ancestry,
6655            code_unit.clone(),
6656            declaration.range,
6657            Some(declaration.declarator),
6658            None,
6659            None,
6660        );
6661        if definition.is_some() {
6662            let recovered_declaration = *self
6663                .source_links
6664                .get(&code_unit)
6665                .and_then(|links| links.last())
6666                .expect("recovered definition source");
6667            let mut properties = self.source_properties.borrow_mut();
6668            let fact = properties
6669                .get_mut(&recovered_declaration)
6670                .expect("recovered definition source fact");
6671            fact.occurrence_role =
6672                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6673            fact.callable_is_constructor |= is_constructor;
6674        }
6675        let signature = normalize_cpp_whitespace(
6676            self.source
6677                .get(declaration.range.start_byte..declaration.range.end_byte)
6678                .expect("a recovered declaration range covers one source range"),
6679        );
6680        let linkage = if declaration.is_static {
6681            CallableLinkage::Internal
6682        } else {
6683            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
6684        };
6685        // Whether this is a definition is a property of the recovered node, not
6686        // of the caller: a declarator that a `function_definition` gives a body
6687        // is a definition wherever the recovery found it.
6688        let declaration_only = definition.is_none();
6689        self.add_signature_with_metadata(
6690            code_unit.clone(),
6691            cpp_signature_metadata(
6692                signature,
6693                declaration.declarator,
6694                linkage,
6695                self.source,
6696                ancestry,
6697            )
6698            .with_declaration_only(declaration_only),
6699        );
6700        if already_declared {
6701            return;
6702        }
6703        if let Some(parent) = &scope.class_unit {
6704            self.parsed.add_child(parent.clone(), code_unit);
6705        } else if let Some(module) = &scope.module {
6706            self.parsed.add_child(module.clone(), code_unit);
6707        }
6708    }
6709
6710    /// Index only anonymous aggregate declarations from an ordinary
6711    /// function body. Function bodies are otherwise outside the declaration
6712    /// walk, but their anonymous aggregate owners must exist so structured
6713    /// local binding inference can name their fields (#2994).
6714    fn visit_anonymous_local_aggregates_in_function<'tree>(
6715        &mut self,
6716        function: Node<'tree>,
6717        scope: &ScopeInfo,
6718        stack: &mut Vec<CppWork<'tree>>,
6719        ancestry: &ParentIndex<'tree>,
6720    ) {
6721        let Some(body) = cpp_body_node(function) else {
6722            return;
6723        };
6724        // A body opens a block scope, so nothing declared in it is a member of
6725        // the class the definition may be written inside. Dropping the
6726        // aggregate carrier gives an inline member function's body the same
6727        // block scope an out-of-line `void C::f()` body already has; without
6728        // it a C++ header, where member bodies are usually inline, would keep
6729        // none of these owners (#3301).
6730        let block_scope;
6731        let scope = if scope.class_unit.is_some() {
6732            block_scope = ScopeInfo {
6733                class_unit: None,
6734                declarations_are_fields: false,
6735                ..scope.clone()
6736            };
6737            &block_scope
6738        } else {
6739            scope
6740        };
6741        let mut pending = vec![body];
6742        while let Some(node) = pending.pop() {
6743            if matches!(node.kind(), "function_definition" | "lambda_expression") {
6744                continue;
6745            }
6746            if node.kind() == "declaration"
6747                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
6748            {
6749                continue;
6750            }
6751            if matches!(
6752                node.kind(),
6753                "class_specifier" | "struct_specifier" | "union_specifier"
6754            ) {
6755                continue;
6756            }
6757            let mut cursor = node.walk();
6758            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
6759            children.reverse();
6760            pending.extend(children);
6761        }
6762    }
6763
6764    fn visit_error_swallowed_function_declaration<'tree>(
6765        &mut self,
6766        node: Node<'tree>,
6767        scope: &ScopeInfo,
6768    ) -> bool {
6769        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
6770            return false;
6771        };
6772        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6773            return false;
6774        };
6775        let root = tree.root_node();
6776        let mut cursor = root.walk();
6777        let declarations = root
6778            .named_children(&mut cursor)
6779            .filter(|child| child.kind() != "comment")
6780            .collect::<Vec<_>>();
6781        let [declaration] = declarations.as_slice() else {
6782            return false;
6783        };
6784        if declaration.kind() != "declaration"
6785            || declaration.has_error()
6786            || declaration.start_byte() != start
6787            || declaration.end_byte() != end
6788        {
6789            return false;
6790        }
6791        let recovery = cpp_recovery_window(self.source, start, end);
6792        // The reparsed region is its own tree, so this walk indexes it itself.
6793        let reparsed_ancestry = self.recovery_ancestry(root);
6794        self.record_recovered_declarations(recovery, node, |visitor| {
6795            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6796        });
6797        true
6798    }
6799
6800    /// Recover a K&R-compatibility prototype macro invocation such as
6801    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
6802    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
6803    /// defined only in headers outside this workspace (ruby's own
6804    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
6805    /// `name _((args))` and turns each such line into a malformed
6806    /// `declaration` or pointer-expression statement. Before this
6807    /// recovery, the ordinary declaration visitor read that wreckage as a
6808    /// field with the type and name swapped (e.g. a field literally named
6809    /// `VALUE`) and indexed no prototype for the real name at all.
6810    ///
6811    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
6812    /// preserve the declared name, a known one-argument compatibility macro,
6813    /// the nested argument list, and a live terminator. It rejects arbitrary
6814    /// identifiers and bare `ERROR` envelopes that flattened those relations;
6815    /// guessing there previously turned malformed non-macro declarations into
6816    /// invented functions. For each proven candidate this reparses the range
6817    /// before the macro token, the inner parenthesized argument node, and the
6818    /// terminating `;` as one included-range parse. That is the parser-native
6819    /// equivalent of expanding `#define _(args) args` while retaining original
6820    /// byte offsets. The result is admitted only when it is exactly one clean
6821    /// function prototype spanning the recovered run.
6822    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6823        for candidate in cpp_prototype_macro_candidates(node, self.source) {
6824            let start = candidate.run_start;
6825            let end = candidate.semicolon_end;
6826            if self.byte_range_is_inside_consumed_fragment(start, end) {
6827                continue;
6828            }
6829            let Some(tree) = parse_source_ranges_with_cancellation(
6830                &tree_sitter_cpp::LANGUAGE.into(),
6831                self.source,
6832                &candidate.ranges(),
6833                None,
6834            ) else {
6835                continue;
6836            };
6837            let root = tree.root_node();
6838            let mut cursor = root.walk();
6839            let declarations = root
6840                .named_children(&mut cursor)
6841                .filter(|child| child.kind() != "comment")
6842                .collect::<Vec<_>>();
6843            let [declaration] = declarations.as_slice() else {
6844                continue;
6845            };
6846            if declaration.kind() != "declaration"
6847                || declaration.has_error()
6848                || declaration.start_byte() != start
6849                || declaration.end_byte() != end
6850                || declaration
6851                    .child_by_field_name("declarator")
6852                    .and_then(extract_function_declarator)
6853                    .is_none()
6854            {
6855                continue;
6856            }
6857            let recovery = cpp_recovery_window(self.source, start, end);
6858            // The reparsed region is its own tree, so this walk indexes it
6859            // itself.
6860            let reparsed_ancestry = self.recovery_ancestry(root);
6861            self.record_recovered_declarations(recovery, node, |visitor| {
6862                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
6863            });
6864            self.consumed_fragment_regions.push((start, end));
6865        }
6866    }
6867
6868    /// Declare one Module per namespace level of `levels` under `package_name`,
6869    /// each recording the range of the construct that writes it, and return the
6870    /// innermost level's package name and Module. A level an earlier definition
6871    /// already declared is reused, which is also how a reopened namespace keeps
6872    /// the range of the definition that first declared it.
6873    fn declare_namespace_levels(
6874        &mut self,
6875        mut package_name: String,
6876        levels: Vec<(String, Range)>,
6877        node: Node<'_>,
6878        ancestry: &ParentIndex<'_>,
6879    ) -> (String, Option<CodeUnit>) {
6880        let mut module = None;
6881        for (component, range) in levels {
6882            let full_name = if package_name.is_empty() {
6883                component
6884            } else {
6885                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
6886            };
6887            let level = CodeUnit::new_fq(
6888                self.file.clone(),
6889                CodeUnitType::Module,
6890                "",
6891                full_name.clone(),
6892                cpp_namespace_fq(&full_name),
6893            );
6894            if !self.parsed.contains_declaration(&level) {
6895                self.add_declaration_with_range(
6896                    ancestry,
6897                    level.clone(),
6898                    range,
6899                    Some(node),
6900                    None,
6901                    None,
6902                );
6903            }
6904            package_name = full_name;
6905            module = Some(level);
6906        }
6907        (package_name, module)
6908    }
6909
6910    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
6911    /// index restores. Each level carries the range of its own name token,
6912    /// which the walk's current node cannot supply: that node is whichever
6913    /// declaration the walk reached first inside the region, and deriving a
6914    /// level's name from it paired the level's range with an unrelated
6915    /// construct's name token (#4292).
6916    fn declare_recovered_namespace_levels(
6917        &mut self,
6918        mut package_name: String,
6919        levels: Vec<(String, Range, Range)>,
6920    ) -> (String, Option<CodeUnit>) {
6921        let mut module = None;
6922        for (component, range, name_range) in levels {
6923            let full_name = if package_name.is_empty() {
6924                component
6925            } else {
6926                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
6927            };
6928            let level = CodeUnit::new_fq(
6929                self.file.clone(),
6930                CodeUnitType::Module,
6931                "",
6932                full_name.clone(),
6933                cpp_namespace_fq(&full_name),
6934            );
6935            if !self.parsed.contains_declaration(&level) {
6936                self.add_declaration_with_name_range(level.clone(), range, name_range, None, None);
6937            }
6938            package_name = full_name;
6939            module = Some(level);
6940        }
6941        (package_name, module)
6942    }
6943
6944    /// Restore the complete lexical namespace path when recovery dropped an
6945    /// ancestor or retained it past its real close. Parsed namespaces beginning
6946    /// inside the recovered region still contribute their normal nesting.
6947    /// Crossing that boundary also clears any malformed class owner.
6948    fn recovered_namespace_scope(
6949        &mut self,
6950        node: Node<'_>,
6951        scope: &ScopeInfo,
6952    ) -> Option<ScopeInfo> {
6953        self.orphaned_namespaces.region_at(node.start_byte())?;
6954        // Each level carries the `namespace Name { ... }` construct that writes
6955        // it along with the level's own name token. Declaring them at `node`
6956        // instead recorded the range of whichever member the walk happened to
6957        // reach first, so a namespace whose head collapsed into an ERROR
6958        // reported its first inner declaration's lines as its own (#3309), and
6959        // deriving the name from that node paired the level's range with the
6960        // member's name token (#4292).
6961        let levels = self
6962            .orphaned_namespaces
6963            .enclosing_namespace_levels(node, self.source);
6964        let package_name = levels
6965            .iter()
6966            .map(|(component, ..)| component.as_str())
6967            .collect::<Vec<_>>()
6968            .join(CPP_PACKAGE_SEPARATOR);
6969        if package_name == scope.package_name {
6970            return None;
6971        }
6972        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
6973        Some(ScopeInfo {
6974            package_name,
6975            module,
6976            // A recovered namespace body is no longer inside the class (or
6977            // function) that tree-sitter accidentally used as its wrapper.
6978            // Keep namespace/module context, but never carry that owner into
6979            // declarations published from the recovered region.
6980            class_unit: None,
6981            template_signature: None,
6982            template_metadata: None,
6983            declarations_are_fields: false,
6984            recovered_specialization_member_scope: false,
6985            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6986        })
6987    }
6988
6989    fn visit_namespace<'tree>(
6990        &mut self,
6991        node: Node<'tree>,
6992        scope: &ScopeInfo,
6993        stack: &mut Vec<CppWork<'tree>>,
6994        ancestry: &ParentIndex<'tree>,
6995    ) {
6996        let name_node = node.child_by_field_name("name");
6997        let Some(name_node) = name_node else {
6998            if let Some(body) = cpp_body_node(node) {
6999                stack.push(CppWork::Container(CppContainer {
7000                    node: body,
7001                    scope: scope.clone(),
7002                }));
7003            }
7004            return;
7005        };
7006        // Diagnostic corpora contain deliberately ill-formed global namespace
7007        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
7008        // the leading global `::` as the first anonymous child. Honor that AST
7009        // boundary instead of appending the name to the lexical namespace;
7010        // appending produced legacy names such as `outer::::outer::inner`, which
7011        // could not round-trip through the structured FqName boundary.
7012        let explicitly_global = name_node
7013            .child(0)
7014            .is_some_and(|child| !child.is_named() && child.kind() == "::");
7015        let components = cpp_namespace_name_components(name_node, self.source);
7016        if components.is_empty() {
7017            return;
7018        }
7019        // One Module per namespace level. C++17's `namespace a::b { ... }` is
7020        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
7021        // shorthand must declare `a` as well as `a::b` -- extracting only the
7022        // innermost level left the enclosing namespace undeclared and made the
7023        // two spellings of one construct disagree (issue #1878).
7024        let package_name = if explicitly_global {
7025            String::new()
7026        } else {
7027            scope.package_name.clone()
7028        };
7029        let definition = node_range(node);
7030        let (package_name, module) = self.declare_namespace_levels(
7031            package_name,
7032            components
7033                .into_iter()
7034                .map(|component| (component, definition))
7035                .collect(),
7036            node,
7037            ancestry,
7038        );
7039
7040        let namespace_scope = ScopeInfo {
7041            package_name,
7042            module,
7043            // C++ never nests a namespace inside a class, so a surviving
7044            // class_unit here is always recovery bleed: a malformed-region
7045            // boundary upstream mis-scoped this namespace block. Keeping the
7046            // owner would mint the namespace's declarations as class members
7047            // under a re-appended package, desyncing the fq boundary assert
7048            // (#2306). Dropping it is identity-neutral for valid code, where
7049            // class_unit is always empty at a namespace definition.
7050            class_unit: None,
7051            template_signature: scope.template_signature.clone(),
7052            template_metadata: scope.template_metadata.clone(),
7053            declarations_are_fields: false,
7054            recovered_specialization_member_scope: false,
7055            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7056        };
7057        let container = cpp_body_node(node).unwrap_or(node);
7058        // A malformed export-macro class body may turn the following class
7059        // into a descendant of a bogus function/labeled/error envelope. Those
7060        // descendants are not declaration containers and the ordinary walk
7061        // intentionally does not descend into them. Scan the namespace tree
7062        // once for the strict embedded class geometry before scheduling its
7063        // normal declarations. When one envelope matches, its helper recovers
7064        // every embedded class and the walk need not inspect its descendants.
7065        let mut candidates = vec![container];
7066        while let Some(candidate) = candidates.pop() {
7067            if matches!(
7068                candidate.kind(),
7069                "ERROR" | "function_definition" | "labeled_statement"
7070            ) && self.visit_embedded_function_like_export_classes(
7071                candidate,
7072                &namespace_scope,
7073                stack,
7074                ancestry,
7075            ) {
7076                continue;
7077            }
7078            for index in (0..candidate.named_child_count()).rev() {
7079                candidates.push(
7080                    candidate
7081                        .named_child(index)
7082                        .expect("index below the node's own named child count"),
7083                );
7084            }
7085        }
7086        stack.push(CppWork::Container(CppContainer {
7087            node: container,
7088            scope: namespace_scope,
7089        }));
7090    }
7091
7092    fn visit_class_like<'tree>(
7093        &mut self,
7094        node: Node<'tree>,
7095        scope: &ScopeInfo,
7096        stack: &mut Vec<CppWork<'tree>>,
7097        ancestry: &ParentIndex<'tree>,
7098    ) {
7099        let Some(name) = class_like_name(node, self.source, ancestry) else {
7100            return;
7101        };
7102        let name = qualified_class_name_chain(node, self.source, scope)
7103            .map(|chain| chain.join("$"))
7104            .unwrap_or(name);
7105        self.visit_named_class_like(node, name, scope, stack, ancestry);
7106    }
7107
7108    fn visit_named_class_like<'tree>(
7109        &mut self,
7110        node: Node<'tree>,
7111        name: String,
7112        scope: &ScopeInfo,
7113        stack: &mut Vec<CppWork<'tree>>,
7114        ancestry: &ParentIndex<'tree>,
7115    ) {
7116        let body = cpp_body_node(node);
7117        let definition_body_present = body.is_some();
7118        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
7119            .then(|| extract_cpp_supertypes(node, self.source));
7120        self.visit_named_class_like_shape(
7121            node,
7122            name,
7123            body,
7124            definition_body_present,
7125            None,
7126            raw_supertypes,
7127            Some(extract_cpp_base_facts(node, self.source)),
7128            scope,
7129            stack,
7130            ancestry,
7131        );
7132    }
7133
7134    /// Whether this class-like declaration is a C tag that belongs to the
7135    /// enclosing non-aggregate scope rather than to the aggregate it is
7136    /// lexically written inside.
7137    ///
7138    /// `class_specifier` is deliberately excluded: `class` is not C, so text
7139    /// that spells one in a `.c` file is not C code and keeps the C++ reading
7140    /// rather than getting a half-C identity.
7141    fn mints_tag_at_enclosing_c_scope(
7142        &self,
7143        declaration_node: Node<'_>,
7144        scope: &ScopeInfo,
7145        ancestry: &ParentIndex<'_>,
7146    ) -> bool {
7147        if scope.class_unit.is_none()
7148            || !matches!(
7149                declaration_node.kind(),
7150                "struct_specifier" | "union_specifier" | "enum_specifier"
7151            )
7152            || class_like_name(declaration_node, self.source, ancestry).is_none()
7153        {
7154            return false;
7155        }
7156        // A class-specifier ancestor proves this is C++ syntax. A C reading of
7157        // the header must not apply C tag hoisting to structs nested in that
7158        // class, because that invents a file-scope identity for its members.
7159        let mut ancestor = ancestry.parent(declaration_node);
7160        while let Some(current) = ancestor {
7161            if current.kind() == "class_specifier" {
7162                return false;
7163            }
7164            ancestor = ancestry.parent(current);
7165        }
7166        true
7167    }
7168
7169    #[allow(clippy::too_many_arguments)]
7170    fn visit_named_class_like_shape<'tree>(
7171        &mut self,
7172        declaration_node: Node<'tree>,
7173        name: String,
7174        body: Option<Node<'tree>>,
7175        definition_body_present: bool,
7176        explicit_range: Option<Range>,
7177        raw_supertypes: Option<Vec<String>>,
7178        base_facts: Option<Vec<CppBaseSpecifierFact>>,
7179        scope: &ScopeInfo,
7180        stack: &mut Vec<CppWork<'tree>>,
7181        ancestry: &ParentIndex<'tree>,
7182    ) -> CodeUnit {
7183        let displaced_macro_tail = if explicit_range.is_none() {
7184            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
7185        } else {
7186            None
7187        };
7188        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
7189        let recovered_scope = self.scope_for_recovered_exported_class(
7190            declaration_node,
7191            &name,
7192            definition_body_present,
7193            scope,
7194            ancestry,
7195        );
7196        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
7197        // aggregate's member list is declared at the enclosing non-aggregate
7198        // scope, not nested inside the aggregate. `scope.class_unit` is the
7199        // only aggregate carrier in this walk, so dropping it puts the tag at
7200        // the nearest enclosing non-aggregate scope -- the module at file or
7201        // namespace scope, and the same block-scope representation a
7202        // function-local aggregate already gets. The tag's own body scope
7203        // below still owns its members, so fields and enumerators are
7204        // unaffected.
7205        let c_tag_scope;
7206        let c_tag_scope_applies =
7207            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
7208        if !self.c_tag_semantics && c_tag_scope_applies {
7209            self.c_tag_scope_witness = true;
7210        }
7211        let scope = if self.c_tag_semantics && c_tag_scope_applies {
7212            c_tag_scope = ScopeInfo {
7213                class_unit: None,
7214                ..recovered_scope.clone()
7215            };
7216            &c_tag_scope
7217        } else {
7218            &recovered_scope
7219        };
7220        let short_name = if let Some(parent) = &scope.class_unit {
7221            cpp_join_nested_short(parent.short_name(), &name)
7222        } else {
7223            name.clone()
7224        };
7225        // A top-level out-of-line qualified class definition (`struct
7226        // Outer::Inner { ... }` inside its namespace, #2246) carries its
7227        // nesting chain as the `$`-joined display name; push one Type/Nested
7228        // segment per class so segment-pop owner navigation keeps working.
7229        // Every other leaf name stays opaque so a literal `$` in a source
7230        // identifier never crosses the split/join boundary (#2140).
7231        let qualified_chain = if scope.class_unit.is_none() {
7232            qualified_class_name_chain(declaration_node, self.source, scope)
7233                .filter(|chain| chain.join("$") == name)
7234        } else {
7235            None
7236        };
7237        let fq = if let Some(chain) = qualified_chain {
7238            let mut fq = FqName::new();
7239            cpp_push_package(&mut fq, &scope.package_name);
7240            let mut first = true;
7241            for component in chain {
7242                let kind = if first {
7243                    SegmentKind::Type
7244                } else {
7245                    SegmentKind::Nested
7246                };
7247                fq.push(segment_interner().intern(&component, kind));
7248                first = false;
7249            }
7250            fq
7251        } else {
7252            cpp_leaf_fq(
7253                &scope.package_name,
7254                scope.class_unit.as_ref(),
7255                &name,
7256                SegmentKind::Nested,
7257                SegmentKind::Type,
7258            )
7259        };
7260        let code_unit = CodeUnit::with_signature_and_fq(
7261            self.file.clone(),
7262            CodeUnitType::Class,
7263            scope.package_name.clone(),
7264            short_name,
7265            scope.template_signature.clone(),
7266            false,
7267            fq,
7268        );
7269        let has_body = definition_body_present;
7270        if !has_body && self.parsed.contains_declaration(&code_unit) {
7271            if let Some(range) = explicit_range {
7272                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
7273            } else {
7274                self.link_source_declaration(ancestry, &code_unit, declaration_node);
7275            }
7276            self.parsed.record_navigation_range(
7277                code_unit.clone(),
7278                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
7279            );
7280            return code_unit;
7281        }
7282        if has_body {
7283            // A forward declaration above this definition is one of the
7284            // class's declaration sites: C++ injects the class name at the
7285            // forward declaration, so a reference between the two spells this
7286            // class (#3297). Moving the unit to its body must not drop the
7287            // earlier site, exactly as a function prototype and its body stay
7288            // two ranges of one declaration (#1650).
7289            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
7290            if let Some(range) = explicit_range {
7291                self.replace_declaration_with_range_deferred(
7292                    ancestry,
7293                    code_unit.clone(),
7294                    range,
7295                    Some(declaration_node),
7296                    None,
7297                    None,
7298                );
7299            } else {
7300                self.replace_declaration_deferred(
7301                    ancestry,
7302                    code_unit.clone(),
7303                    declaration_node,
7304                    None,
7305                    None,
7306                );
7307            }
7308            for range in earlier_ranges {
7309                self.parsed.add_declaration_range(&code_unit, range);
7310            }
7311        } else {
7312            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
7313        }
7314        if let Some(declaration) = self
7315            .source_links
7316            .get(&code_unit)
7317            .and_then(|links| links.last())
7318            .copied()
7319        {
7320            let mut properties = self.source_properties.borrow_mut();
7321            let fact = properties
7322                .get_mut(&declaration)
7323                .expect("linked class has source properties");
7324            fact.occurrence_role = if has_body {
7325                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
7326            } else {
7327                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
7328            };
7329            fact.class_strength = if has_body {
7330                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
7331            } else {
7332                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
7333            };
7334            if let Some(base_facts) = base_facts.as_ref() {
7335                fact.bases = base_facts.clone();
7336            }
7337        }
7338        if let Some(raw_supertypes) = raw_supertypes {
7339            self.parsed
7340                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
7341        }
7342        self.parsed.add_signature(
7343            code_unit.clone(),
7344            render_cpp_type_signature(
7345                declaration_node,
7346                self.source,
7347                scope.template_signature.as_deref(),
7348            ),
7349        );
7350        if let Some(metadata) = &scope.template_metadata {
7351            let primary_short_name = if let Some(parent) = &scope.class_unit {
7352                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
7353            } else {
7354                metadata.primary_name.clone()
7355            };
7356            let primary_fq_name = CodeUnit::new(
7357                self.file.clone(),
7358                CodeUnitType::Class,
7359                scope.package_name.clone(),
7360                primary_short_name,
7361            )
7362            .fq_name();
7363            let mut metadata = metadata.clone();
7364            metadata.primary_fq_name = primary_fq_name;
7365            self.parsed
7366                .set_cpp_template_metadata(code_unit.clone(), metadata);
7367        }
7368        if let Some(parent) = &scope.class_unit {
7369            self.parsed.add_child(parent.clone(), code_unit.clone());
7370        } else if let Some(module) = &scope.module {
7371            self.parsed.add_child(module.clone(), code_unit.clone());
7372        }
7373
7374        if let Some(body) = body {
7375            let mut nested_scope = scope.clone();
7376            nested_scope.class_unit = Some(code_unit.clone());
7377            nested_scope.template_signature = scope.template_signature.clone();
7378            // Template metadata describes the class just created. It must not
7379            // leak into ordinary nested declarations in that class's body.
7380            // Recovered export-macro specializations carry a separate scope bit
7381            // for their declaration-shaped body members.
7382            nested_scope.template_metadata = None;
7383            // Export-macro class bodies recovered from a function_definition use
7384            // compound_statement children, whose direct fields are declarations.
7385            nested_scope.recovered_specialization_member_scope =
7386                scope.template_metadata.as_ref().is_some_and(|metadata| {
7387                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
7388                });
7389            nested_scope.declarations_are_fields =
7390                is_recovered_exported_class_container(declaration_node, self.source)
7391                    || nested_scope.recovered_specialization_member_scope;
7392            if let Some(displaced) = displaced_macro_tail {
7393                // A macro-shaped field without a source semicolon can make
7394                // tree-sitter consume the real class terminator as an ERROR
7395                // inside that field, then retain following namespace items as
7396                // later field-list children. Drain the proven class prefix
7397                // first and re-own only the structured tail with the outer
7398                // scope. The tail is pushed first because the work stack is
7399                // LIFO.
7400                push_cpp_sibling_range(
7401                    body,
7402                    displaced.split_index,
7403                    usize::MAX,
7404                    scope.clone(),
7405                    stack,
7406                );
7407                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
7408            } else {
7409                stack.push(CppWork::Container(CppContainer {
7410                    node: body,
7411                    scope: nested_scope,
7412                }));
7413            }
7414        }
7415        if declaration_node.kind() == "enum_specifier" {
7416            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
7417            if !self.has_enum_enumerator_units(&code_unit) {
7418                self.visit_enum_enumerators_from_text(
7419                    ancestry,
7420                    declaration_node,
7421                    scope,
7422                    &code_unit,
7423                );
7424            }
7425        }
7426        code_unit
7427    }
7428
7429    /// Whether the parse product already holds enumerator fields for `parent`,
7430    /// answered from the walk's carried-forward field ownership index.
7431    ///
7432    /// Built on the first enum's question and advanced by every declaration
7433    /// recorded after it, so a file that declares no enum -- most files -- pays
7434    /// nothing, and one that declares thousands pays a single pass instead of
7435    /// one per enum (#2786).
7436    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
7437        if self.field_owners.is_none() {
7438            self.field_owners = Some(CppFieldOwnerIndex::of(
7439                self.parsed.declarations().iter(),
7440                self.file,
7441            ));
7442        }
7443        debug_assert_eq!(
7444            parent.source(),
7445            self.file,
7446            "the walk's declarations are declarations of the file it is walking"
7447        );
7448        let carried = self
7449            .field_owners
7450            .as_ref()
7451            .expect("the index was just ensured")
7452            .owns_fields(parent.package_name(), parent.short_name());
7453
7454        #[cfg(debug_assertions)]
7455        assert_eq!(
7456            carried,
7457            cpp_declarations_hold_owned_fields(
7458                self.parsed.declarations(),
7459                self.file,
7460                parent.package_name(),
7461                parent.short_name()
7462            ),
7463            "the carried-forward field index must answer what a fresh declaration scan \
7464             answers for {}",
7465            parent.fq_name()
7466        );
7467
7468        carried
7469    }
7470
7471    fn visit_enum_enumerators(
7472        &mut self,
7473        ancestry: &ParentIndex<'_>,
7474        node: Node<'_>,
7475        scope: &ScopeInfo,
7476        parent: &CodeUnit,
7477    ) {
7478        walk_named_tree_preorder(node, false, |child| {
7479            if child.kind() != "enumerator" {
7480                return WalkControl::Continue;
7481            }
7482            let Some(name_node) = child.child_by_field_name("name") else {
7483                return WalkControl::Continue;
7484            };
7485            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
7486            if name.is_empty() {
7487                return WalkControl::Continue;
7488            }
7489            let signature = normalize_cpp_whitespace(node_text(child, self.source));
7490            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
7491            WalkControl::Continue
7492        });
7493    }
7494
7495    fn visit_enum_enumerator(
7496        &mut self,
7497        ancestry: &ParentIndex<'_>,
7498        declaration_node: Node<'_>,
7499        scope: &ScopeInfo,
7500        parent: &CodeUnit,
7501        name: &str,
7502        signature: &str,
7503    ) {
7504        let code_unit = CodeUnit::new_fq(
7505            self.file.clone(),
7506            CodeUnitType::Field,
7507            scope.package_name.clone(),
7508            cpp_join_member_short(parent.short_name(), name),
7509            parent
7510                .fq()
7511                .clone()
7512                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
7513        );
7514        if self.parsed.contains_declaration(&code_unit) {
7515            return;
7516        }
7517        self.add_declaration(
7518            ancestry,
7519            code_unit.clone(),
7520            declaration_node,
7521            Some(parent.clone()),
7522            None,
7523        );
7524        self.parsed.add_signature(code_unit, signature.to_string());
7525    }
7526
7527    fn visit_enum_enumerators_from_text(
7528        &mut self,
7529        ancestry: &ParentIndex<'_>,
7530        node: Node<'_>,
7531        scope: &ScopeInfo,
7532        parent: &CodeUnit,
7533    ) {
7534        let text = node_text(node, self.source);
7535        let Some((_, body)) = text.split_once('{') else {
7536            return;
7537        };
7538        let Some((body, _)) = body.rsplit_once('}') else {
7539            return;
7540        };
7541        for entry in body.split(',') {
7542            let trimmed = entry.trim();
7543            let name = trimmed
7544                .split('=')
7545                .next()
7546                .unwrap_or("")
7547                .split_whitespace()
7548                .next()
7549                .unwrap_or("");
7550            if name.is_empty() {
7551                continue;
7552            }
7553            self.visit_enum_enumerator(ancestry, node, scope, parent, name, trimmed);
7554        }
7555    }
7556
7557    fn visit_function_definition<'tree>(
7558        &mut self,
7559        node: Node<'tree>,
7560        scope: &ScopeInfo,
7561        stack: &mut Vec<CppWork<'tree>>,
7562        ancestry: &ParentIndex<'tree>,
7563    ) {
7564        // An attribute-like macro invocation whose argument is a declaration can
7565        // swallow every declaration written after it into one bogus
7566        // `function_definition` (#2551). This owns the whole region, so it runs
7567        // ahead of `visit_macro_swallowed_function_declarations` below, which
7568        // admits the same envelope by its head macro token and reads single
7569        // declarators out of nodes this recovery reparses properly.
7570        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
7571            return;
7572        }
7573        // A file-scope object-like macro sentinel the parser cannot see (issue
7574        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
7575        // prefixes as a bogus `function_definition` that swallows real namespaces,
7576        // classes, and members. Reparse the swallowed interior as C++ items so the
7577        // ordinary declaration visitors index it with byte/line-exact ownership.
7578        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
7579            return;
7580        }
7581        if node.has_error() {
7582            self.visit_macro_swallowed_function_declarations(node, scope);
7583        }
7584        if let Some((class_node, name, raw_supertypes, base_facts)) =
7585            recover_exported_class_function_definition(node, self.source)
7586        {
7587            if let Some(body) = cpp_body_node(node)
7588                && let Some(close) = self
7589                    .orphaned_namespaces
7590                    .matching_close_brace(body.start_byte())
7591                && close.end_byte < body.end_byte()
7592            {
7593                let class_range = Range {
7594                    start_byte: node.start_byte(),
7595                    end_byte: close.end_byte,
7596                    start_line: node.start_position().row + 1,
7597                    end_line: close.end_line,
7598                };
7599                // Parse the body in class context, preserving the original
7600                // keyword, recovered name and body as included ranges. Parsing
7601                // the interior at file scope turns constructors and access
7602                // labels into another oversized ERROR instead of members.
7603                let mut head = vec![node];
7604                let mut keyword = None;
7605                let mut name_node = None;
7606                while let Some(part) = head.pop() {
7607                    if part.start_byte() >= body.start_byte() || part.is_missing() {
7608                        continue;
7609                    }
7610                    if matches!(part.kind(), "class" | "struct" | "union") {
7611                        keyword = Some(part);
7612                    }
7613                    if matches!(part.kind(), "identifier" | "type_identifier")
7614                        && node_text(part, self.source) == name
7615                    {
7616                        name_node = Some(part);
7617                    }
7618                    let mut cursor = part.walk();
7619                    head.extend(part.children(&mut cursor));
7620                }
7621                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
7622                    && let Some(type_name) = keyword
7623                        .parent()
7624                        .and_then(|parent| parent.child_by_field_name("name"))
7625                    && let Some(tree) = parse_source_ranges_with_cancellation(
7626                        &tree_sitter_cpp::LANGUAGE.into(),
7627                        self.source,
7628                        &[
7629                            (keyword.start_byte(), type_name.start_byte()),
7630                            (name_node.start_byte(), name_node.end_byte()),
7631                            (body.start_byte(), close.end_byte),
7632                        ],
7633                        None,
7634                    )
7635                    && let Some(reparsed_class) = tree.root_node().named_child(0)
7636                    && let Some(class_body) = cpp_body_node(reparsed_class)
7637                    && class_body.start_byte() == body.start_byte()
7638                    && class_body.end_byte() == close.end_byte
7639                    && let Some(tail) =
7640                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
7641                {
7642                    let class_body_range = class_body.byte_range();
7643                    self.with_recovery_source_context(node, |visitor| {
7644                        let source_context = visitor.active_source_context.clone();
7645                        let class_unit = visitor.visit_named_class_like_shape(
7646                            class_node,
7647                            name,
7648                            None,
7649                            true,
7650                            Some(class_range),
7651                            raw_supertypes,
7652                            base_facts,
7653                            scope,
7654                            stack,
7655                            ancestry,
7656                        );
7657                        visitor.parsed.record_materialization(
7658                            MaterializationRecord::RecoveredDeclaration {
7659                                recovery: class_range,
7660                                unit: class_unit.clone(),
7661                            },
7662                        );
7663                        let member_scope = ScopeInfo {
7664                            package_name: class_unit.package_name().to_string(),
7665                            class_unit: Some(class_unit),
7666                            declarations_are_fields: true,
7667                            template_metadata: None,
7668                            recovered_specialization_member_scope: false,
7669                            ..scope.clone()
7670                        };
7671                        let tail_range = tail.root_node().byte_range();
7672                        let class_body_recovery = cpp_recovery_window(
7673                            visitor.source,
7674                            class_body_range.start,
7675                            class_body_range.end,
7676                        );
7677                        let tail_recovery =
7678                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
7679                        visitor.partitioned_regions.push(CppPartitionedRegion {
7680                            tree: tail,
7681                            range: tail_range,
7682                            scope: scope.clone(),
7683                            recovery: tail_recovery,
7684                            source_context: source_context.clone(),
7685                        });
7686                        visitor.partitioned_regions.push(CppPartitionedRegion {
7687                            tree,
7688                            range: class_body_range,
7689                            scope: member_scope,
7690                            recovery: class_body_recovery,
7691                            source_context: source_context.clone(),
7692                        });
7693                    });
7694                    return;
7695                }
7696            }
7697            let body = cpp_body_node(class_node);
7698            let displaced_namespace = cpp_body_node(node)
7699                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
7700            let fragmented = cpp_body_node(node).and_then(|body| {
7701                fragmented_export_function_body_region(
7702                    node,
7703                    body,
7704                    self.source,
7705                    displaced_namespace.as_ref(),
7706                )
7707            });
7708            // The recovery tuple's first node is the class-like type when the
7709            // parser exposes one, but the synthetic wrapper owns the compound
7710            // statement that contains the truncated class body. Use the
7711            // wrapper body for fragmented-member detection; retain the
7712            // class-node body for the ordinary (non-fragmented) path below.
7713            if let Some(fragmented) = fragmented {
7714                // The lifted sibling no longer sits below the parser-visible
7715                // namespace node. Restore the current parent scope when the
7716                // ordinary work walk reaches that class.
7717                if let Some(boundary) =
7718                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
7719                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
7720                {
7721                    let mut boundary_scope = scope.clone();
7722                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
7723                        if sibling.start_byte() >= boundary.start_byte() {
7724                            break;
7725                        }
7726                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
7727                            boundary_scope.visible_using_namespaces.push(namespace);
7728                        }
7729                    }
7730                    self.recovered_class_sibling_scopes
7731                        .insert(boundary.id(), boundary_scope);
7732                }
7733                let mut recovered_constructor = None;
7734                let mut recovered_prefix_tree = None;
7735                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
7736                {
7737                    Some(FragmentedExportMembers::Complete(tree)) => {
7738                        if let Some(body) = body
7739                            && let Some(range) =
7740                                cpp_reparsed_synthetic_initializer_constructor_range(
7741                                    tree.root_node(),
7742                                    &name,
7743                                    self.source,
7744                                    body.end_byte(),
7745                                )
7746                        {
7747                            recovered_constructor = Some(range);
7748                            recovered_prefix_tree = Some(tree);
7749                            None
7750                        } else {
7751                            Some(FragmentedExportMembers::Complete(tree))
7752                        }
7753                    }
7754                    outcome => outcome,
7755                };
7756                let mut class_stack = Vec::new();
7757                let class_unit = self.visit_named_class_like_shape(
7758                    class_node,
7759                    name,
7760                    None,
7761                    true,
7762                    Some(fragmented.class_range),
7763                    raw_supertypes,
7764                    base_facts.clone(),
7765                    scope,
7766                    &mut class_stack,
7767                    ancestry,
7768                );
7769                self.parsed
7770                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
7771                        recovery: fragmented.class_range,
7772                        unit: class_unit.clone(),
7773                    });
7774                let complete = outcome.is_some_and(|outcome| {
7775                    self.visit_fragmented_export_class_members(
7776                        node,
7777                        outcome,
7778                        class_unit.clone(),
7779                        scope,
7780                    )
7781                });
7782                if complete {
7783                    self.consumed_fragment_regions
7784                        .push((node.start_byte(), fragmented.class_range.end_byte));
7785                } else {
7786                    // The reparse can fail when the first constructor or a
7787                    // method body is split into statement-shaped siblings.
7788                    // Keep the recovered class envelope, but do not visit the
7789                    // synthetic wrapper body: its initializer expressions can
7790                    // look like same-named member functions (for example
7791                    // `Token.location(loc)`). Re-own only the original sibling
7792                    // nodes that fall inside the proven class range. Their CST
7793                    // shapes retain the real field/function kinds and ranges.
7794                    let member_scope = ScopeInfo {
7795                        package_name: class_unit.package_name().to_string(),
7796                        module: scope.module.clone(),
7797                        class_unit: Some(class_unit.clone()),
7798                        template_signature: scope.template_signature.clone(),
7799                        template_metadata: None,
7800                        declarations_are_fields: true,
7801                        recovered_specialization_member_scope: false,
7802                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
7803                    };
7804                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
7805                        if candidate.start_byte() >= fragmented.reparse_end {
7806                            break;
7807                        }
7808                        if cpp_fragment_sibling_is_class_member(
7809                            candidate,
7810                            fragmented.reparse_end,
7811                            self.source,
7812                        ) {
7813                            self.recovered_class_sibling_scopes
7814                                .insert(candidate.id(), member_scope.clone());
7815                        }
7816                    }
7817                    if let Some(range) = recovered_constructor
7818                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
7819                    {
7820                        let prefix_root = prefix_tree.root_node();
7821                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
7822                        self.visit_recovered_fragment_prefix_members(
7823                            node,
7824                            prefix_root,
7825                            range.start,
7826                            &class_unit,
7827                            scope,
7828                            &prefix_ancestry,
7829                        );
7830                        self.visit_recovered_fragment_constructor(
7831                            range,
7832                            body,
7833                            class_node,
7834                            &class_unit,
7835                            scope,
7836                            ancestry,
7837                        );
7838                    }
7839                }
7840                if let Some(boundary) = displaced_namespace {
7841                    for item in boundary.namespace_items {
7842                        self.recovered_class_sibling_scopes
7843                            .insert(item.id(), scope.clone());
7844                    }
7845                }
7846                stack.extend(class_stack);
7847                return;
7848            }
7849            let mut stack = Vec::new();
7850            let class_unit = self.visit_named_class_like_shape(
7851                class_node,
7852                name,
7853                body,
7854                body.is_some(),
7855                None,
7856                raw_supertypes,
7857                base_facts,
7858                scope,
7859                &mut stack,
7860                ancestry,
7861            );
7862            self.parsed
7863                .record_materialization(MaterializationRecord::RecoveredDeclaration {
7864                    recovery: cpp_declaration_range(node),
7865                    unit: class_unit,
7866                });
7867            // Issue #1524: the bogus `function_definition` body can run past
7868            // the class's true closing brace (the parse ends it with a
7869            // zero-width `MISSING "}"`), swallowing following namespace-scope
7870            // siblings -- they would index as members of the recovered class.
7871            // When the body's text-balanced close lands before the body's own
7872            // end, re-own the swallowed tail with the outer scope instead.
7873            if let Some(body) = body
7874                && let Some(class_close) = self
7875                    .orphaned_namespaces
7876                    .matching_close_brace(body.start_byte())
7877                && class_close.start_byte < body.end_byte()
7878            {
7879                let split = {
7880                    let mut cursor = body.walk();
7881                    body.named_children(&mut cursor)
7882                        .position(|child| child.start_byte() > class_close.start_byte)
7883                };
7884                if let Some(split) = split {
7885                    // The seeded work is a single Container over the whole
7886                    // body with the class scope; replace it with the bounded
7887                    // head (class scope) plus the swallowed tail (outer
7888                    // scope). Push tail first so the head drains first.
7889                    let seeded = stack.pop();
7890                    match seeded {
7891                        Some(CppWork::Container(container)) => {
7892                            push_cpp_sibling_range(
7893                                body,
7894                                split,
7895                                usize::MAX,
7896                                scope.clone(),
7897                                &mut stack,
7898                            );
7899                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
7900                        }
7901                        // visit_named_class_like_shape always seeds exactly
7902                        // one Container when a body is present.
7903                        _ => unreachable!("exported-class seed is always one Container"),
7904                    }
7905                }
7906            }
7907            while let Some(work) = stack.pop() {
7908                match work {
7909                    CppWork::Container(container) => {
7910                        push_cpp_container_work(container.node, container.scope, &mut stack);
7911                    }
7912                    CppWork::Siblings(siblings) => {
7913                        advance_cpp_siblings(siblings, self.source, &mut stack);
7914                    }
7915                    CppWork::Node(work) => {
7916                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
7917                    }
7918                }
7919            }
7920            return;
7921        }
7922        let recovered_constraint_constructor =
7923            cpp_recovered_template_macro_constructor(node, self.source);
7924        let declarator = recovered_constraint_constructor
7925            .map(|(declarator, _)| declarator)
7926            .or_else(|| node.child_by_field_name("declarator"));
7927        let Some(declarator) = declarator else {
7928            self.visit_malformed_function_definition_container(node, scope, stack);
7929            return;
7930        };
7931        let Some(function_declarator) = extract_function_declarator(declarator) else {
7932            self.visit_malformed_function_definition_container(node, scope, stack);
7933            return;
7934        };
7935        let function = if let Some((_, callable_name)) =
7936            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
7937        {
7938            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
7939        } else {
7940            extract_function_info(function_declarator, self.source, scope)
7941        };
7942        let Some(mut function) = function else {
7943            self.visit_malformed_function_definition_container(node, scope, stack);
7944            return;
7945        };
7946        if let Some((_, template_parameter)) = recovered_constraint_constructor {
7947            function.signature = format!(
7948                "template <{}>{}",
7949                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
7950                function.signature
7951            );
7952        }
7953        let code_unit = function.code_unit(self.file.clone());
7954        // Keep an earlier same-file prototype as another physical occurrence
7955        // of this callable. `CodeUnit` already identifies the role-neutral
7956        // overload, while ranges and signature metadata describe its
7957        // declaration/definition occurrences.
7958        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
7959        let signature = if recovered_constraint_constructor.is_some() {
7960            normalize_cpp_whitespace(node_text(function_declarator, self.source))
7961        } else {
7962            render_cpp_function_display_signature_from_node(
7963                node,
7964                self.source,
7965                scope.template_signature.as_deref(),
7966                true,
7967                ancestry,
7968            )
7969        };
7970        self.add_signature_with_metadata(
7971            code_unit.clone(),
7972            cpp_signature_metadata(
7973                signature,
7974                function_declarator,
7975                cpp_callable_linkage(node, self.source, ancestry),
7976                self.source,
7977                ancestry,
7978            )
7979            .with_declaration_only(false),
7980        );
7981        if let Some(parent) = &scope.class_unit {
7982            self.parsed.add_child(parent.clone(), code_unit);
7983        } else if let Some(module) = &scope.module {
7984            self.parsed.add_child(module.clone(), code_unit);
7985        }
7986        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
7987    }
7988
7989    /// Recover the namespace lost when tree-sitter promotes an export-macro
7990    /// class definition to a root-level `function_definition`.  Only a
7991    /// body-bearing, top-level recovery may borrow a namespace, and only when
7992    /// one earlier namespace-scope forward declaration proves the identity.
7993    fn scope_for_recovered_exported_class<'tree>(
7994        &mut self,
7995        node: Node<'tree>,
7996        name: &str,
7997        definition_body_present: bool,
7998        scope: &ScopeInfo,
7999        ancestry: &ParentIndex<'tree>,
8000    ) -> ScopeInfo {
8001        if !definition_body_present
8002            || !scope.package_name.is_empty()
8003            || scope.class_unit.is_some()
8004            || !(is_recovered_exported_class_container(node, self.source)
8005                || recover_function_like_export_class_pair(node, self.source).is_some()
8006                || recover_embedded_function_like_export_classes(node, self.source)
8007                    .iter()
8008                    .any(|recovered| recovered.name == name)
8009                || matches!(node.kind(), "declaration" | "field_declaration")
8010                    && recover_exported_class_declaration(node, self.source).is_some()
8011                || matches!(
8012                    node.kind(),
8013                    "class_specifier" | "struct_specifier" | "union_specifier"
8014                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
8015                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
8016                        name_node,
8017                        self.source,
8018                    )))
8019                }) || ancestry.parent(node).is_some_and(|parent| {
8020                    matches!(parent.kind(), "declaration" | "field_declaration")
8021                        && recover_exported_class_declaration(parent, self.source).is_some()
8022                        || is_recovered_exported_class_container(parent, self.source)
8023                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
8024        {
8025            return scope.clone();
8026        }
8027        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
8028        let Some(package_name) = borrowed_namespace
8029            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
8030        else {
8031            return scope.clone();
8032        };
8033
8034        let module = CodeUnit::new_fq(
8035            self.file.clone(),
8036            CodeUnitType::Module,
8037            "",
8038            package_name.clone(),
8039            cpp_namespace_fq(&package_name),
8040        );
8041        let mut recovered = scope.clone();
8042        recovered.package_name = package_name;
8043        recovered.module = Some(module);
8044        recovered
8045    }
8046
8047    /// The unique namespace-scope forward declaration of `name` that precedes
8048    /// `recovered_node`, answered from the walk's carried-forward scan of the
8049    /// tree `recovered_node` belongs to.
8050    ///
8051    /// The scan is built on the first question and advanced by each later one,
8052    /// so a file that never reaches this path -- almost every file -- pays
8053    /// nothing, and one that reaches it thousands of times pays a single pass
8054    /// (#2754).
8055    fn unique_earlier_namespace_forward<'tree>(
8056        &mut self,
8057        recovered_node: Node<'tree>,
8058        name: &str,
8059        ancestry: &ParentIndex<'tree>,
8060    ) -> Option<String> {
8061        let mut root = recovered_node;
8062        while let Some(parent) = ancestry.parent(root) {
8063            root = parent;
8064        }
8065        let source = self.source;
8066        let scan = self
8067            .namespace_forward_scans
8068            .entry(CppTreeIdentity::of(root))
8069            .or_default();
8070        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
8071        let borrowed = scan.unique_earlier_forward(name, recovered_node);
8072
8073        #[cfg(debug_assertions)]
8074        assert_eq!(
8075            borrowed,
8076            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
8077            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
8078             for {name} at byte {}",
8079            recovered_node.start_byte()
8080        );
8081
8082        borrowed
8083    }
8084
8085    fn visit_malformed_function_definition_container<'tree>(
8086        &mut self,
8087        node: Node<'tree>,
8088        scope: &ScopeInfo,
8089        stack: &mut Vec<CppWork<'tree>>,
8090    ) {
8091        let Some(body) = cpp_body_node(node) else {
8092            return;
8093        };
8094        if !cpp_contains_namespace_definition(body) {
8095            return;
8096        }
8097        stack.push(CppWork::Container(CppContainer {
8098            node: body,
8099            scope: scope.clone(),
8100        }));
8101    }
8102
8103    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
8104    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
8105    /// emits for a sentinel-prefixed region, reparse the interior after the
8106    /// sentinel identifier as real C++ items -- confined to the region so
8107    /// every reparsed node keeps its original byte/line position -- and run the
8108    /// ordinary container visitation over the result. Returns `true` when it fired
8109    /// (the caller must then skip normal function processing). Nested sentinel
8110    /// regions recover recursively: the reparsed interior is walked through the
8111    /// same `visit_function_definition` path, so a sentinel inside the region hits
8112    /// this recovery again.
8113    /// Runs `reparse_walk` and records every declaration it mints as a
8114    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
8115    /// `recovery` (issue #1657). A reparsed sentinel region has no single
8116    /// recovered envelope unit: the ordinary visitors mint namespaces,
8117    /// classes, and members directly from the reparsed tree, so the walk's
8118    /// declaration delta is the recovered set. Records are ordered by
8119    /// declaration start byte so the parse product stays deterministic.
8120    fn record_recovered_declarations(
8121        &mut self,
8122        recovery: Range,
8123        origin: Node<'_>,
8124        reparse_walk: impl FnOnce(&mut Self),
8125    ) {
8126        self.with_recovery_source_context(origin, |visitor| {
8127            let source_context = visitor.active_source_context.clone();
8128            visitor.record_recovered_declarations_with_context(
8129                recovery,
8130                source_context,
8131                reparse_walk,
8132            );
8133        });
8134    }
8135
8136    /// Run a recovered walk with an owned source context and record every
8137    /// declaration it mints. Deferred partitioned regions cannot retain their
8138    /// origin `Node`, so they capture the context while the origin tree is
8139    /// live and use this same materialization path when drained.
8140    fn record_recovered_declarations_with_context(
8141        &mut self,
8142        recovery: Range,
8143        source_context: Option<CppActiveSourceContext>,
8144        reparse_walk: impl FnOnce(&mut Self),
8145    ) {
8146        // The set difference this used to be, kept as the oracle every answer
8147        // is asserted against (#2787).
8148        #[cfg(any(debug_assertions, test))]
8149        let before = self.parsed.declarations().clone();
8150
8151        self.recovery_captures.push(CppRecoveryCapture::default());
8152        let previous = self.active_source_context.take();
8153        self.active_source_context = source_context;
8154        reparse_walk(self);
8155        self.active_source_context = previous;
8156        let captured = self
8157            .recovery_captures
8158            .pop()
8159            .expect("the capture this call pushed is the one it pops");
8160
8161        // The capture holds every declaration created while it was open, once
8162        // each and in creation order, so the recovered set costs what the
8163        // recovery made rather than everything the file has declared so far.
8164        // One filter is left to apply: a created declaration that a later
8165        // deferred replacement removed is not in the parse product to report.
8166        let mut minted: Vec<CodeUnit> = captured
8167            .created
8168            .into_iter()
8169            .filter(|unit| self.parsed.contains_declaration(unit))
8170            .collect();
8171        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
8172
8173        #[cfg(any(debug_assertions, test))]
8174        {
8175            let mut rediscovered: Vec<CodeUnit> = self
8176                .parsed
8177                .declarations()
8178                .iter()
8179                .filter(|unit| !before.contains(*unit))
8180                .cloned()
8181                .collect();
8182            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
8183            assert_eq!(
8184                minted, rediscovered,
8185                "the captured recovered set must be the declaration delta of the reparse \
8186                 walk over {recovery:?}"
8187            );
8188        }
8189
8190        for unit in minted {
8191            self.parsed
8192                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8193                    recovery,
8194                    unit,
8195                });
8196        }
8197    }
8198
8199    /// Where one recovered declaration sorts: by start byte, then by name, so
8200    /// the parse product stays deterministic.
8201    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
8202        let start = self
8203            .parsed
8204            .declaration_ranges(unit)
8205            .first()
8206            .map(|range| range.start_byte)
8207            .unwrap_or(usize::MAX);
8208        (start, unit.fq_name().to_string())
8209    }
8210
8211    fn visit_sentinel_macro_region<'tree>(
8212        &mut self,
8213        node: Node<'tree>,
8214        scope: &ScopeInfo,
8215        stack: &mut Vec<CppWork<'tree>>,
8216        ancestry: &ParentIndex<'tree>,
8217    ) -> bool {
8218        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
8219            return true;
8220        }
8221        if let Some((
8222            reparse_start,
8223            class_start,
8224            body_start,
8225            class_close_start,
8226            class_close_end,
8227            class_close_line,
8228        )) = cpp_sentinel_macro_class_region(node, self.source)
8229        {
8230            let Some(class_tree) =
8231                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
8232            else {
8233                return false;
8234            };
8235            let class_root = class_tree.root_node();
8236            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
8237            // A region reparse is its own tree and needs its own parent index.
8238            let class_ancestry = self.recovery_ancestry(class_root);
8239            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
8240                class_root,
8241                template_node,
8242                self.source,
8243                &class_ancestry,
8244            ) else {
8245                return false;
8246            };
8247            let class_node = reparsed_class.declaration_node;
8248            let name = reparsed_class.name;
8249            let mut class_scope = scope.clone();
8250            if let Some(template_node) = template_node {
8251                class_scope.template_signature =
8252                    cpp_template_signature(template_node, class_node, self.source);
8253                class_scope.template_metadata =
8254                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
8255            }
8256            let Some(body_tree) =
8257                cpp_reparse_region_items(self.source, body_start, class_close_start)
8258            else {
8259                return false;
8260            };
8261            let raw_supertypes = reparsed_class.raw_supertypes;
8262            let base_facts = reparsed_class.base_facts;
8263            let class_range = Range {
8264                start_byte: class_start,
8265                end_byte: class_close_end,
8266                start_line: class_node.start_position().row + 1,
8267                end_line: class_close_line,
8268            };
8269            let class_scope = self.scope_for_recovered_exported_class(
8270                class_node,
8271                &name,
8272                true,
8273                &class_scope,
8274                &class_ancestry,
8275            );
8276            let mut class_stack = Vec::new();
8277            self.with_recovery_source_context(node, |visitor| {
8278                let class_unit = visitor.visit_named_class_like_shape(
8279                    class_node,
8280                    name,
8281                    None,
8282                    true,
8283                    Some(class_range),
8284                    raw_supertypes,
8285                    base_facts,
8286                    &class_scope,
8287                    &mut class_stack,
8288                    &class_ancestry,
8289                );
8290                visitor.parsed.record_materialization(
8291                    MaterializationRecord::RecoveredDeclaration {
8292                        recovery: class_range,
8293                        unit: class_unit.clone(),
8294                    },
8295                );
8296                let member_scope = ScopeInfo {
8297                    package_name: class_scope.package_name.clone(),
8298                    module: class_scope.module.clone(),
8299                    class_unit: Some(class_unit),
8300                    template_signature: class_scope.template_signature.clone(),
8301                    template_metadata: None,
8302                    declarations_are_fields: true,
8303                    recovered_specialization_member_scope: false,
8304                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
8305                };
8306                // The padded body reparse is its own tree, so it indexes itself.
8307                let body_root = body_tree.root_node();
8308                visitor.run_container_work(
8309                    body_root,
8310                    member_scope,
8311                    &visitor.recovery_ancestry(body_root),
8312                );
8313            });
8314            // Register only after the padded body reparse: its nodes deliberately
8315            // retain offsets inside the consumed region and must be visited first.
8316            self.consumed_fragment_regions
8317                .push((node.start_byte(), class_close_end));
8318            // An ERROR envelope can hold real sibling declarations after the
8319            // recovered class's close (the suffix-reparse boundary in
8320            // `cpp_sentinel_macro_class_region` partitions, it does not
8321            // consume). Walk the envelope's remaining children normally; the
8322            // consumed region above keeps the recovered class from being
8323            // indexed twice.
8324            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
8325                stack.push(CppWork::Container(CppContainer {
8326                    node,
8327                    scope: scope.clone(),
8328                }));
8329            }
8330            return true;
8331        }
8332        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
8333            return false;
8334        };
8335        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
8336            return false;
8337        };
8338        let root = tree.root_node();
8339        if !cpp_reparsed_items_are_indexable(root, self.source) {
8340            return false;
8341        }
8342        let recovery = cpp_recovery_window(self.source, start, end);
8343        // The reparsed region is its own tree, so this walk indexes it itself.
8344        let reparsed_ancestry = self.recovery_ancestry(root);
8345        self.record_recovered_declarations(recovery, node, |visitor| {
8346            visitor.visit_container(
8347                root,
8348                &reparsed_ancestry,
8349                &scope.package_name,
8350                scope.module.clone(),
8351                scope.class_unit.clone(),
8352                scope.template_signature.clone(),
8353                scope.visible_using_namespaces.clone(),
8354            );
8355        });
8356        if end > node.end_byte() {
8357            self.consumed_fragment_regions
8358                .push((node.start_byte(), end));
8359        } else if node.kind() == "ERROR" && node.end_byte() > end {
8360            // The sentinel region ended at the first recovered class-like item
8361            // but the ERROR envelope keeps real sibling declarations after it
8362            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
8363            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
8364            // envelope's remaining children normally; the consumed region
8365            // keeps the reparsed prefix from being indexed twice.
8366            self.consumed_fragment_regions
8367                .push((node.start_byte(), end));
8368            stack.push(CppWork::Container(CppContainer {
8369                node,
8370                scope: scope.clone(),
8371            }));
8372        }
8373        true
8374    }
8375
8376    /// Re-own complete class declarations from the structured Abseil
8377    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
8378    /// its direct CST children already prove both namespace components and the
8379    /// class bodies, so the ordinary class/member visitor can retain ownership
8380    /// and exact source ranges without admitting unrelated callable bodies.
8381    fn visit_nested_namespace_sentinel<'tree>(
8382        &mut self,
8383        node: Node<'tree>,
8384        scope: &ScopeInfo,
8385        ancestry: &ParentIndex<'tree>,
8386    ) -> bool {
8387        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
8388            return false;
8389        };
8390
8391        let mut package_name = scope.package_name.clone();
8392        let mut module = scope.module.clone();
8393        for component in recovered.namespace_components {
8394            package_name = if package_name.is_empty() {
8395                component
8396            } else {
8397                format!("{package_name}::{component}")
8398            };
8399            let namespace_module = CodeUnit::new_fq(
8400                self.file.clone(),
8401                CodeUnitType::Module,
8402                "",
8403                package_name.clone(),
8404                cpp_namespace_fq(&package_name),
8405            );
8406            if !self.parsed.contains_declaration(&namespace_module) {
8407                self.add_declaration(
8408                    ancestry,
8409                    namespace_module.clone(),
8410                    recovered.function,
8411                    None,
8412                    None,
8413                );
8414            }
8415            module = Some(namespace_module);
8416        }
8417
8418        let recovered_scope = ScopeInfo {
8419            package_name,
8420            module,
8421            // This scope's package comes from the namespace levels the sentinel
8422            // shape proves, so its owner must come from the same place. C++
8423            // never nests a namespace inside a class, so a surviving
8424            // `class_unit` here is recovery bleed from an earlier malformed
8425            // region, and keeping it would publish declarations whose package
8426            // names one lexical position and whose owner names another -- the
8427            // hybrid identity that trips the package/short boundary assert in
8428            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
8429            // identity-neutral for valid code, where `class_unit` is always
8430            // empty at a namespace.
8431            class_unit: None,
8432            template_signature: scope.template_signature.clone(),
8433            template_metadata: scope.template_metadata.clone(),
8434            declarations_are_fields: false,
8435            recovered_specialization_member_scope: false,
8436            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8437        };
8438        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
8439            recovered.function,
8440            recovered.body,
8441            self.source,
8442            ancestry,
8443        ) {
8444            let mut class_scope = recovered_scope.clone();
8445            if let Some(template_node) = fragmented.template_node {
8446                class_scope.template_signature =
8447                    cpp_template_signature(template_node, fragmented.class_node, self.source);
8448                class_scope.template_metadata = cpp_template_metadata(
8449                    template_node,
8450                    fragmented.class_node,
8451                    self.source,
8452                    ancestry,
8453                );
8454            }
8455            if let Some(outcome) = self
8456                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
8457            {
8458                let mut class_stack = Vec::new();
8459                let class_unit = self.visit_named_class_like_shape(
8460                    fragmented.class_node,
8461                    fragmented.name.clone(),
8462                    None,
8463                    true,
8464                    Some(fragmented.fragmented.class_range),
8465                    fragmented.raw_supertypes.clone(),
8466                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
8467                    &class_scope,
8468                    &mut class_stack,
8469                    ancestry,
8470                );
8471                self.parsed
8472                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8473                        recovery: fragmented.fragmented.class_range,
8474                        unit: class_unit.clone(),
8475                    });
8476                if self.visit_fragmented_export_class_members(
8477                    node,
8478                    outcome,
8479                    class_unit,
8480                    &class_scope,
8481                ) {
8482                    self.consumed_fragment_regions.push((
8483                        fragmented.consumed_start,
8484                        fragmented.fragmented.class_range.end_byte,
8485                    ));
8486                }
8487            }
8488        }
8489        // The class requirement above is the admission gate; once admitted,
8490        // traverse the whole proven inner namespace body so sibling aliases,
8491        // functions, and variables are not silently discarded. The body is a
8492        // node of the tree being walked, so it reuses that tree's index.
8493        self.run_container_work(recovered.body, recovered_scope, ancestry);
8494        true
8495    }
8496
8497    fn visit_declaration<'tree>(
8498        &mut self,
8499        node: Node<'tree>,
8500        scope: &ScopeInfo,
8501        in_class_body: bool,
8502        stack: &mut Vec<CppWork<'tree>>,
8503        ancestry: &ParentIndex<'tree>,
8504    ) {
8505        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8506            return;
8507        }
8508        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
8509            return;
8510        }
8511        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
8512            !cpp_active_template_type_parameter(
8513                node,
8514                node_text(declarator, self.source),
8515                self.source,
8516                ancestry,
8517            )
8518        }) {
8519            return;
8520        }
8521        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
8522            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
8523            // the following `Imaging image` member into one malformed field.
8524            // The ERROR's identifier is the actual declarator; route it
8525            // through the ordinary field path so its parent, range, and
8526            // signature metadata remain consistent with every other member.
8527            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
8528            return;
8529        }
8530        if in_class_body
8531            && let Some(parent) = scope.class_unit.as_ref()
8532            && let Some(call) =
8533                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
8534        {
8535            self.visit_recovered_macro_qualified_constructor_definition(
8536                node, call, scope, ancestry,
8537            );
8538            return;
8539        }
8540        if in_class_body
8541            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
8542        {
8543            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
8544            return;
8545        }
8546        if in_class_body
8547            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
8548        {
8549            for member in members {
8550                self.add_macro_wrapped_declaration(member, scope, ancestry);
8551            }
8552            return;
8553        }
8554        if in_class_body
8555            && let Some(declarators) =
8556                recovered_macro_qualified_field_declarators(node, self.source)
8557        {
8558            for declarator in declarators {
8559                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8560            }
8561            return;
8562        }
8563        let recovered_alias_names = recovered_type_alias_names(node, self.source);
8564        if !recovered_alias_names.is_empty() {
8565            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
8566            return;
8567        }
8568        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
8569            return;
8570        }
8571        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
8572            return;
8573        }
8574
8575        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
8576            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
8577                // Issue #938: the members tree-sitter scattered out of the fragmented
8578                // multiple-base export node are reparsed from their true body region
8579                // and re-owned as members of the recovered class, with an explicit
8580                // navigation range spanning to the displaced closing brace.
8581                if let Some(outcome) =
8582                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
8583                {
8584                    let consumed_region = (
8585                        recovered.declaration_node.end_byte(),
8586                        fragmented.class_range.end_byte,
8587                    );
8588                    let code_unit = self.visit_named_class_like_shape(
8589                        recovered.declaration_node,
8590                        recovered.name,
8591                        None,
8592                        true,
8593                        Some(fragmented.class_range),
8594                        recovered.raw_supertypes,
8595                        recovered.base_facts,
8596                        scope,
8597                        stack,
8598                        ancestry,
8599                    );
8600                    self.parsed.record_materialization(
8601                        MaterializationRecord::RecoveredDeclaration {
8602                            recovery: fragmented.class_range,
8603                            unit: code_unit.clone(),
8604                        },
8605                    );
8606                    let consume_fragment =
8607                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
8608                    // Everything between the fragmented declaration and its displaced
8609                    // closing brace now belongs to the recovered class; keep the
8610                    // ordinary walk from re-indexing those scattered siblings at top
8611                    // level. Register the consumed region only after indexing because
8612                    // the reparsed nodes retain byte offsets inside that same region.
8613                    if consume_fragment {
8614                        self.consumed_fragment_regions.push(consumed_region);
8615                    }
8616                    return;
8617                }
8618            }
8619            let uses_initializer_body = recovered.uses_initializer_body;
8620            let definition_body_present = recovered.body.is_some();
8621            let class_unit = self.visit_named_class_like_shape(
8622                recovered.declaration_node,
8623                recovered.name,
8624                recovered.body,
8625                definition_body_present,
8626                None,
8627                recovered.raw_supertypes,
8628                recovered.base_facts,
8629                scope,
8630                stack,
8631                ancestry,
8632            );
8633            self.parsed
8634                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8635                    recovery: cpp_declaration_range(node),
8636                    unit: class_unit,
8637                });
8638            if uses_initializer_body {
8639                return;
8640            }
8641        }
8642
8643        // A function-like macro invocation that expands to declarations keeps
8644        // no terminator of its own: the semicolons live in the replacement
8645        // body, which is not part of this tree. Tree-sitter recovers one and
8646        // reads the macro name as the member type and its argument list as a
8647        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
8648        // published a field named `ClosedDetect` beside the real constructor
8649        // and split every reference to the constructor's name between the two
8650        // (#3298). The invented terminator is the whole difference from a real
8651        // member: a member terminates itself, and with a terminator the
8652        // grammar reads `T (name);` as a declaration rather than a field.
8653        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
8654            return;
8655        }
8656
8657        let mut handled_function = false;
8658        let mut handled_declarator = false;
8659        let mut cursor = node.walk();
8660        for child in node.named_children(&mut cursor) {
8661            if matches!(
8662                child.kind(),
8663                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8664            ) {
8665                // A named class-like definition remains a declaration even when
8666                // the same statement also declares an object, for example
8667                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
8668                // declaration's type and `kind` as its declarator.  Dropping the
8669                // type here loses both its nested owner and every later lexical
8670                // reference to it.  A body is the structured proof that this is
8671                // a definition rather than an elaborated type use such as
8672                // `class Kind value;`.
8673                if cpp_body_node(child).is_some() {
8674                    self.visit_class_like(child, scope, stack, ancestry);
8675                }
8676                continue;
8677            }
8678        }
8679
8680        let mut cursor = node.walk();
8681        for child in node.children_by_field_name("declarator", &mut cursor) {
8682            if crate::structural::is_recovered_designator_init_declarator(child) {
8683                handled_declarator = true;
8684                continue;
8685            }
8686            if in_class_body
8687                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
8688            {
8689                handled_declarator = true;
8690                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
8691                continue;
8692            }
8693            if let Some(kind) = classify_declarator(child) {
8694                handled_declarator = true;
8695                match kind {
8696                    DeclaratorKind::Function(function_declarator) => {
8697                        handled_function = true;
8698                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
8699                    }
8700                    DeclaratorKind::Variable(variable_declarator) => {
8701                        self.visit_variable_declaration(
8702                            node,
8703                            variable_declarator,
8704                            scope,
8705                            in_class_body,
8706                            ancestry,
8707                        );
8708                    }
8709                }
8710            }
8711        }
8712
8713        if !handled_declarator {
8714            let mut cursor = node.walk();
8715            for child in node.named_children(&mut cursor) {
8716                if crate::structural::is_recovered_designator_init_declarator(child) {
8717                    handled_declarator = true;
8718                    continue;
8719                }
8720                if !is_unfielded_declarator_candidate(child) {
8721                    continue;
8722                }
8723                let Some(kind) = classify_declarator(child) else {
8724                    continue;
8725                };
8726                handled_declarator = true;
8727                match kind {
8728                    DeclaratorKind::Function(function_declarator) => {
8729                        handled_function = true;
8730                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
8731                    }
8732                    DeclaratorKind::Variable(variable_declarator) => {
8733                        self.visit_variable_declaration(
8734                            node,
8735                            variable_declarator,
8736                            scope,
8737                            in_class_body,
8738                            ancestry,
8739                        );
8740                    }
8741                }
8742            }
8743        }
8744
8745        if handled_function {
8746            return;
8747        }
8748
8749        if !handled_declarator {
8750            if in_class_body {
8751                self.visit_class_members_from_declaration(node, scope, ancestry);
8752            } else {
8753                self.visit_global_variables_from_declaration(node, scope, ancestry);
8754            }
8755        }
8756    }
8757
8758    /// Preserve the member structure of an anonymous aggregate.
8759    ///
8760    /// An anonymous union with no declarator promotes its fields into the
8761    /// containing aggregate. An anonymous struct/union followed by a named
8762    /// declarator, such as `struct { T *ops; } sock`, declares both the field
8763    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
8764    /// the declarator's structured nested identity so a later `value.sock.ops`
8765    /// chain can traverse it without parsing a type spelling (#2407).
8766    ///
8767    /// An anonymous aggregate declares no tag, so there is nothing for the C
8768    /// dialect to re-scope and nothing for the C++ dialect to nest: both
8769    /// readings of this shape are the same, and both need these members
8770    /// (#3301).
8771    fn anonymous_aggregate_declaration_parts<'tree>(
8772        node: Node<'tree>,
8773    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
8774        let aggregate = node.child_by_field_name("type")?;
8775        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
8776            || aggregate.child_by_field_name("name").is_some()
8777        {
8778            return None;
8779        }
8780        let body = cpp_body_node(aggregate)?;
8781        let mut cursor = node.walk();
8782        let declarators = node
8783            .children_by_field_name("declarator", &mut cursor)
8784            .filter_map(|declarator| match classify_declarator(declarator) {
8785                Some(DeclaratorKind::Variable(variable)) => Some(variable),
8786                Some(DeclaratorKind::Function(_)) | None => None,
8787            })
8788            .collect();
8789        Some((aggregate, body, declarators))
8790    }
8791
8792    fn visit_anonymous_aggregate_declaration<'tree>(
8793        &mut self,
8794        node: Node<'tree>,
8795        scope: &ScopeInfo,
8796        in_class_body: bool,
8797        stack: &mut Vec<CppWork<'tree>>,
8798        ancestry: &ParentIndex<'tree>,
8799    ) -> bool {
8800        if !in_class_body || scope.class_unit.is_none() {
8801            return false;
8802        }
8803        let Some((aggregate, body, declarators)) =
8804            Self::anonymous_aggregate_declaration_parts(node)
8805        else {
8806            return false;
8807        };
8808        if declarators.is_empty() {
8809            stack.push(CppWork::Container(CppContainer {
8810                node: body,
8811                scope: scope.clone(),
8812            }));
8813            return true;
8814        }
8815
8816        for declarator in declarators {
8817            let Some(name) = extract_variable_name(declarator, self.source) else {
8818                continue;
8819            };
8820            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
8821            // The receiver type's identity is the declarator's name, so its
8822            // range is the field declaration that carries that name token.
8823            // The inner specifier ends at the closing brace, which leaves the
8824            // name the unit is known by outside its own text (#3287).
8825            self.visit_named_class_like_shape(
8826                aggregate,
8827                name,
8828                Some(body),
8829                true,
8830                Some(cpp_declaration_range(node)),
8831                None,
8832                None,
8833                scope,
8834                stack,
8835                ancestry,
8836            );
8837        }
8838        true
8839    }
8840
8841    /// Give a function-local anonymous aggregate a structured owner so its
8842    /// direct pointer bindings can be typed by the usage graph.  Unlike an
8843    /// anonymous aggregate in a class body, there is no source-level tag or
8844    /// typedef to supply an identity.  The aggregate's CST range is therefore
8845    /// part of a generated, collision-resistant identity; no source spelling
8846    /// is consulted.  The declaration's own variable remains a file-level
8847    /// field projection, while the aggregate body is visited as the generated
8848    /// class's field list.
8849    fn visit_anonymous_local_aggregate_declaration<'tree>(
8850        &mut self,
8851        node: Node<'tree>,
8852        scope: &ScopeInfo,
8853        stack: &mut Vec<CppWork<'tree>>,
8854        ancestry: &ParentIndex<'tree>,
8855    ) -> bool {
8856        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
8857            return false;
8858        }
8859        let Some((aggregate, body, declarators)) =
8860            Self::anonymous_aggregate_declaration_parts(node)
8861        else {
8862            return false;
8863        };
8864        if declarators.is_empty() {
8865            return false;
8866        }
8867
8868        for declarator in &declarators {
8869            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
8870        }
8871        let name = format!("<anonymous:{}>", aggregate.start_byte());
8872        self.visit_named_class_like_shape(
8873            aggregate,
8874            name,
8875            Some(body),
8876            true,
8877            None,
8878            None,
8879            None,
8880            scope,
8881            stack,
8882            ancestry,
8883        );
8884        true
8885    }
8886
8887    fn visit_function_declaration<'tree>(
8888        &mut self,
8889        declaration_node: Node<'tree>,
8890        declarator: Node<'tree>,
8891        scope: &ScopeInfo,
8892        ancestry: &ParentIndex<'tree>,
8893    ) {
8894        let Some(function) = extract_function_info(declarator, self.source, scope) else {
8895            return;
8896        };
8897        // An in-class member prototype is a real source declaration, not a
8898        // generated or recovery-only unit, so it keeps the ordinary code-unit
8899        // identity (#3508). #3493 recorded its callable modifiers, which is
8900        // what a canonical procedure key needs; a synthetic unit would still
8901        // refuse that key on identity grounds alone.
8902        let code_unit = function.code_unit(self.file.clone());
8903        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
8904            return;
8905        }
8906        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8907        let signature = render_cpp_function_display_signature_from_node(
8908            declaration_node,
8909            self.source,
8910            scope.template_signature.as_deref(),
8911            false,
8912            ancestry,
8913        );
8914        self.add_signature_with_metadata(
8915            code_unit.clone(),
8916            cpp_signature_metadata(
8917                signature,
8918                declarator,
8919                cpp_callable_linkage(declaration_node, self.source, ancestry),
8920                self.source,
8921                ancestry,
8922            )
8923            .with_declaration_only(true),
8924        );
8925        if let Some(parent) = &scope.class_unit {
8926            self.parsed.add_child(parent.clone(), code_unit);
8927        } else if let Some(module) = &scope.module {
8928            self.parsed.add_child(module.clone(), code_unit);
8929        }
8930    }
8931
8932    fn link_existing_source_declaration(
8933        &mut self,
8934        ancestry: &ParentIndex<'_>,
8935        code_unit: &CodeUnit,
8936        declaration_node: Node<'_>,
8937    ) -> bool {
8938        if !self.parsed.contains_declaration(code_unit) {
8939            return false;
8940        }
8941        self.link_source_declaration(ancestry, code_unit, declaration_node);
8942        self.parsed
8943            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
8944        true
8945    }
8946
8947    fn visit_recovered_macro_qualified_function_declaration<'tree>(
8948        &mut self,
8949        declaration_node: Node<'tree>,
8950        call: Node<'tree>,
8951        scope: &ScopeInfo,
8952        ancestry: &ParentIndex<'tree>,
8953    ) {
8954        let Some(parent) = &scope.class_unit else {
8955            return;
8956        };
8957        let Some(name_node) = call.child_by_field_name("function") else {
8958            return;
8959        };
8960        let Some(arguments) = call.child_by_field_name("arguments") else {
8961            return;
8962        };
8963        let Some((signature, parameter_labels)) =
8964            recovered_macro_qualified_function_parameters(arguments, self.source)
8965        else {
8966            return;
8967        };
8968        let arity = parameter_labels.len();
8969        let function = FunctionInfo {
8970            package_name: scope.package_name.clone(),
8971            owner: Some(CppMemberOwner::Unit(parent.clone())),
8972            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
8973            signature,
8974        };
8975        if function.name.is_empty() {
8976            return;
8977        }
8978        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
8979        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
8980            return;
8981        }
8982        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8983        let signature_label = render_cpp_function_display_signature_from_node(
8984            declaration_node,
8985            self.source,
8986            scope.template_signature.as_deref(),
8987            false,
8988            ancestry,
8989        );
8990        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
8991        // Only the callable's parameters came from the reparse: the
8992        // `field_declaration` this recovery reads is a real node in a real
8993        // class body, so its storage class and the body's access ladder state
8994        // this member's modifiers exactly as an ordinary member's do.
8995        let modifiers =
8996            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
8997        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
8998            .with_declaration_only(true)
8999            .with_callable_arity(CallableArity::exact(arity))
9000            .with_callable_linkage(linkage)
9001            .with_callable_modifiers(
9002                modifiers.is_static,
9003                modifiers.is_constructor,
9004                modifiers.visibility,
9005            );
9006        self.add_signature_with_metadata(code_unit.clone(), metadata);
9007        self.parsed.add_child(parent.clone(), code_unit);
9008    }
9009
9010    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
9011        &mut self,
9012        declaration_node: Node<'tree>,
9013        call: Node<'tree>,
9014        scope: &ScopeInfo,
9015        ancestry: &ParentIndex<'tree>,
9016    ) {
9017        let Some(parent) = &scope.class_unit else {
9018            return;
9019        };
9020        let Some(arguments) = call.child_by_field_name("arguments") else {
9021            return;
9022        };
9023        let Some((mut signature, parameter_labels)) =
9024            recovered_macro_qualified_function_parameters(arguments, self.source)
9025        else {
9026            return;
9027        };
9028        if let Some(template_signature) = &scope.template_signature {
9029            signature = format!("{template_signature}{signature}");
9030        }
9031        let arity = parameter_labels.len();
9032        let function = FunctionInfo {
9033            package_name: scope.package_name.clone(),
9034            owner: Some(CppMemberOwner::Unit(parent.clone())),
9035            name: parent.identifier().to_string(),
9036            signature,
9037        };
9038        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
9039        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9040        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
9041        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
9042        // The recovery already proved this member is the class's constructor:
9043        // the reparsed call names the enclosing class. Everything else is read
9044        // from the real `field_declaration` the recovery found it in.
9045        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
9046        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
9047            .with_declaration_only(false)
9048            .with_callable_arity(CallableArity::exact(arity))
9049            .with_callable_linkage(linkage)
9050            .with_callable_modifiers(
9051                modifiers.is_static,
9052                modifiers.is_constructor,
9053                modifiers.visibility,
9054            );
9055        self.add_signature_with_metadata(code_unit.clone(), metadata);
9056        self.parsed.add_child(parent.clone(), code_unit);
9057    }
9058
9059    fn visit_variable_declaration<'tree>(
9060        &mut self,
9061        declaration_node: Node<'tree>,
9062        declarator: Node<'tree>,
9063        scope: &ScopeInfo,
9064        in_class_body: bool,
9065        ancestry: &ParentIndex<'tree>,
9066    ) {
9067        let Some(name) = extract_variable_name(declarator, self.source) else {
9068            return;
9069        };
9070        let parent = if in_class_body {
9071            let Some(parent) = &scope.class_unit else {
9072                return;
9073            };
9074            Some(parent)
9075        } else {
9076            None
9077        };
9078        let short_name = match parent {
9079            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
9080            None => name.clone(),
9081        };
9082        let fq = cpp_leaf_fq(
9083            &scope.package_name,
9084            parent,
9085            &name,
9086            SegmentKind::Member,
9087            SegmentKind::Member,
9088        );
9089        let code_unit = CodeUnit::new_fq(
9090            self.file.clone(),
9091            CodeUnitType::Field,
9092            scope.package_name.clone(),
9093            short_name,
9094            fq,
9095        );
9096        if self.parsed.contains_declaration(&code_unit) {
9097            return;
9098        }
9099        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9100        self.add_signature_with_metadata(
9101            code_unit.clone(),
9102            SignatureMetadata::new(
9103                render_cpp_field_signature(declaration_node, declarator, self.source),
9104                Vec::new(),
9105            )
9106            .with_cpp_field_linkage(cpp_field_declaration_linkage(
9107                declaration_node,
9108                self.source,
9109                ancestry,
9110            )),
9111        );
9112        if let Some(parent) = &scope.class_unit {
9113            self.parsed.add_child(parent.clone(), code_unit);
9114        } else if let Some(module) = &scope.module {
9115            self.parsed.add_child(module.clone(), code_unit);
9116        }
9117    }
9118
9119    fn variable_declarator_from_declaration_child<'tree>(
9120        child: Node<'tree>,
9121    ) -> Option<Node<'tree>> {
9122        if child.kind() == "init_declarator" {
9123            child.child_by_field_name("declarator")
9124        } else if matches!(
9125            child.kind(),
9126            "identifier"
9127                | "field_identifier"
9128                | "pointer_declarator"
9129                | "reference_declarator"
9130                | "array_declarator"
9131                | "parenthesized_declarator"
9132        ) {
9133            Some(child)
9134        } else {
9135            None
9136        }
9137    }
9138
9139    fn visit_class_members_from_declaration<'tree>(
9140        &mut self,
9141        node: Node<'tree>,
9142        scope: &ScopeInfo,
9143        ancestry: &ParentIndex<'tree>,
9144    ) {
9145        let mut cursor = node.walk();
9146        for child in node.named_children(&mut cursor) {
9147            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
9148                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
9149            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
9150            {
9151                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9152            }
9153        }
9154    }
9155
9156    fn visit_global_variables_from_declaration<'tree>(
9157        &mut self,
9158        node: Node<'tree>,
9159        scope: &ScopeInfo,
9160        ancestry: &ParentIndex<'tree>,
9161    ) {
9162        let mut cursor = node.walk();
9163        for child in node.named_children(&mut cursor) {
9164            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
9165                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
9166            }
9167        }
9168    }
9169
9170    fn visit_type_declaration<'tree>(
9171        &mut self,
9172        node: Node<'tree>,
9173        scope: &ScopeInfo,
9174        stack: &mut Vec<CppWork<'tree>>,
9175        ancestry: &ParentIndex<'tree>,
9176    ) {
9177        let type_node = node.child_by_field_name("type");
9178        if let Some(type_node) = type_node
9179            && matches!(
9180                type_node.kind(),
9181                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9182            )
9183        {
9184            self.visit_class_like(type_node, scope, stack, ancestry);
9185        }
9186
9187        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
9188            let range = Range {
9189                start_byte: node.start_byte(),
9190                end_byte: recovered.end_node.end_byte(),
9191                start_line: node.start_position().row + 1,
9192                end_line: recovered.end_node.end_position().row + 1,
9193            };
9194            let signature = self
9195                .source
9196                .get(range.start_byte..range.end_byte)
9197                .map(normalize_cpp_whitespace)
9198                .unwrap_or_default();
9199            self.record_type_aliases(
9200                ancestry,
9201                node,
9202                scope,
9203                vec![recovered.name],
9204                signature,
9205                range,
9206            );
9207            return;
9208        }
9209
9210        let alias_names = match node.kind() {
9211            "alias_declaration" => extract_alias_declaration_name(node, self.source)
9212                .into_iter()
9213                .collect::<Vec<_>>(),
9214            "type_definition" => extract_typedef_alias_names(node, self.source),
9215            _ => Vec::new(),
9216        };
9217        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
9218            (type_node, alias_names.as_slice())
9219            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
9220            && type_node.child_by_field_name("name").is_none()
9221        {
9222            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
9223        } else {
9224            None
9225        };
9226        self.add_type_aliases(ancestry, node, scope, alias_names);
9227        if let Some((body, alias_name)) = anonymous_aggregate {
9228            // The typedef alias is also the only user-visible identity of an
9229            // anonymous aggregate. Reuse it as the member owner instead of
9230            // minting a second signatureless class with the same FQN. The
9231            // latter makes forward lookup ambiguous when conditional aliases
9232            // coexist and returns duplicate definitions even without guards.
9233            let signature = normalize_cpp_whitespace(node_text(node, self.source));
9234            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
9235            debug_assert!(self.parsed.contains_declaration(&alias_unit));
9236            let mut nested_scope = scope.clone();
9237            nested_scope.class_unit = Some(alias_unit);
9238            nested_scope.template_signature = scope.template_signature.clone();
9239            nested_scope.template_metadata = None;
9240            nested_scope.declarations_are_fields = false;
9241            nested_scope.recovered_specialization_member_scope = false;
9242            stack.push(CppWork::Container(CppContainer {
9243                node: body,
9244                scope: nested_scope,
9245            }));
9246        }
9247    }
9248
9249    fn add_type_aliases(
9250        &mut self,
9251        ancestry: &ParentIndex<'_>,
9252        node: Node<'_>,
9253        scope: &ScopeInfo,
9254        alias_names: Vec<String>,
9255    ) {
9256        let signature = normalize_cpp_whitespace(node_text(node, self.source));
9257        self.record_type_aliases(
9258            ancestry,
9259            node,
9260            scope,
9261            alias_names,
9262            signature,
9263            cpp_declaration_range(node),
9264        );
9265    }
9266
9267    fn record_type_aliases(
9268        &mut self,
9269        ancestry: &ParentIndex<'_>,
9270        node: Node<'_>,
9271        scope: &ScopeInfo,
9272        alias_names: Vec<String>,
9273        signature: String,
9274        range: Range,
9275    ) {
9276        if signature.is_empty() {
9277            return;
9278        }
9279        let type_name = node
9280            .child_by_field_name("type")
9281            .and_then(|type_node| type_node.child_by_field_name("name"))
9282            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
9283        for alias_name in alias_names {
9284            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
9285                continue;
9286            }
9287            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
9288            // Declaration identity does not include the alias signature. Keep
9289            // each physical range so conditional aliases retain their guards.
9290            self.note_declaration(&code_unit);
9291            self.link_source_declaration(ancestry, &code_unit, node);
9292            self.parsed
9293                .add_code_unit_with_range(code_unit.clone(), range, None, None);
9294            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
9295            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
9296                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
9297            });
9298            self.add_signature_with_metadata(
9299                code_unit.clone(),
9300                SignatureMetadata::new(signature.clone(), Vec::new())
9301                    .with_underlying_type_identity(underlying_type_identity),
9302            );
9303            if let Some(metadata) = &scope.template_metadata {
9304                let mut metadata = metadata.clone();
9305                metadata.primary_fq_name = code_unit.fq_name();
9306                self.parsed
9307                    .set_cpp_template_metadata(code_unit.clone(), metadata);
9308            }
9309            if let Some(parent) = &scope.class_unit {
9310                self.parsed.add_child(parent.clone(), code_unit.clone());
9311            } else if let Some(module) = &scope.module {
9312                self.parsed.add_child(module.clone(), code_unit.clone());
9313            }
9314            self.parsed.mark_type_alias(code_unit);
9315        }
9316    }
9317
9318    fn type_alias_unit(
9319        &self,
9320        scope: &ScopeInfo,
9321        alias_name: String,
9322        signature: String,
9323    ) -> CodeUnit {
9324        let short_name = if let Some(parent) = &scope.class_unit {
9325            cpp_join_nested_short(parent.short_name(), &alias_name)
9326        } else {
9327            alias_name.clone()
9328        };
9329        let fq = cpp_leaf_fq(
9330            &scope.package_name,
9331            scope.class_unit.as_ref(),
9332            &alias_name,
9333            SegmentKind::Nested,
9334            SegmentKind::Type,
9335        );
9336        CodeUnit::with_signature_and_fq(
9337            self.file.clone(),
9338            CodeUnitType::Class,
9339            scope.package_name.clone(),
9340            short_name,
9341            Some(signature),
9342            false,
9343            fq,
9344        )
9345    }
9346
9347    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
9348        // A comment can terminate tree-sitter's preproc_arg before the logical
9349        // directive ends. Its remaining declarations are replacement locals,
9350        // not file-scope fields, even when recovery exposes them as siblings.
9351        if let Some(replacement) =
9352            crate::graph::syntax::function_macro_replacement_span(node, self.source)
9353        {
9354            self.consumed_fragment_regions
9355                .push((replacement.start, replacement.end));
9356        }
9357        let Some(name) = extract_macro_name(node, self.source) else {
9358            return;
9359        };
9360        let signature = node_text(node, self.source).trim_end().to_string();
9361        if signature.is_empty() {
9362            return;
9363        }
9364        let fq = cpp_member_fq("", &name);
9365        // A macro can be undefined and redefined later in the same file. Its
9366        // structured directive is part of the declaration identity so the
9367        // temporal environment can navigate to the definition active at a
9368        // reference instead of collapsing every spelling to the first range.
9369        // The same physical directive parsed through another C/C++ reading
9370        // still produces the same unit and remains deduplicated.
9371        let code_unit = CodeUnit::with_signature_and_fq(
9372            self.file.clone(),
9373            CodeUnitType::Macro,
9374            "",
9375            name.clone(),
9376            Some(signature.clone()),
9377            false,
9378            fq,
9379        );
9380        if !self.parsed.contains_declaration(&code_unit) {
9381            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
9382            let name_range = node
9383                .child_by_field_name("name")
9384                .map(cpp_declaration_range)
9385                .unwrap_or_else(|| cpp_declaration_range(node));
9386            self.parsed
9387                .record_materialization(MaterializationRecord::GeneratedDeclaration {
9388                    site: cpp_declaration_range(node),
9389                    argument: name_range,
9390                    kind: GenerationKind::PreprocessorDefinition,
9391                    unit: code_unit.clone(),
9392                });
9393            self.parsed.add_signature(code_unit, signature);
9394        }
9395        if node.kind() == "preproc_def" {
9396            update_object_macro_field_environment(
9397                node,
9398                self.source,
9399                &mut self.object_macro_fields,
9400                &mut self.ambiguous_object_macro_fields,
9401            );
9402        } else {
9403            self.object_macro_fields.remove(&name);
9404            self.ambiguous_object_macro_fields.remove(&name);
9405        }
9406    }
9407
9408    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9409        let Some(directive) = node.child_by_field_name("directive") else {
9410            return;
9411        };
9412        let name = node_text(directive, self.source).trim();
9413        let range = cpp_declaration_range(node);
9414        let fields = object_macro_field_closure(&self.object_macro_fields, name);
9415        self.materialize_object_macro_fields(fields, range, node, scope);
9416    }
9417
9418    /// Bare object-like field-list macros inside an otherwise well-formed
9419    /// aggregate remain identifier nodes in a field declaration. More than
9420    /// one adjacent invocation can be folded into the same declaration, so
9421    /// inspect all of its structured identifier nodes.
9422    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9423        if !matches!(node.kind(), "declaration" | "field_declaration") {
9424            return false;
9425        }
9426        // Claiming the declaration means its members are accounted for, which
9427        // is only true when there is an owner to materialize them into. A file
9428        // scope reaches this with a declaration that merely *contains* an
9429        // aggregate spelling an invocation (tree-sitter wraps libuv's
9430        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
9431        // `field_declaration` under a malformed header), and consuming it there
9432        // would discard the aggregate itself (#2985).
9433        if scope.class_unit.is_none() {
9434            return false;
9435        }
9436        let macro_nodes =
9437            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9438        for macro_node in &macro_nodes {
9439            let name = node_text(*macro_node, self.source).trim();
9440            let fields = object_macro_field_closure(&self.object_macro_fields, name);
9441            self.materialize_object_macro_fields(
9442                fields,
9443                cpp_declaration_range(*macro_node),
9444                *macro_node,
9445                scope,
9446            );
9447        }
9448        let Some(last) = macro_nodes.last() else {
9449            return false;
9450        };
9451        // Claiming the declaration accounts for every member it spells, not
9452        // only the donated ones: an invocation folded together with the
9453        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
9454        // leaves those members shapeless, and the grammar recovers them from
9455        // the byte range that follows the invocation.
9456        self.record_collapsed_aggregate_fields(
9457            last.end_byte()..node.end_byte(),
9458            node.start_position().row
9459                + 1
9460                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
9461            node,
9462            scope,
9463        );
9464        true
9465    }
9466
9467    /// Retain the grammar's field interpretation with the original invocation
9468    /// or aggregate context. The replacement AST owns the type, while only the
9469    /// original source owns activation, guards, and occurrence ranges.
9470    fn capture_recovered_field_facts(
9471        &mut self,
9472        unit: &CodeUnit,
9473        range: Range,
9474        context: Node<'_>,
9475        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
9476        names_function_type: bool,
9477    ) {
9478        let declaration = *self.source_links[unit]
9479            .last()
9480            .expect("recovered field follows its source occurrence");
9481        let mut fact = crate::source_facts::capture_declaration(
9482            declaration,
9483            context,
9484            unit,
9485            self.source,
9486            self.primary_ancestry,
9487        );
9488        fact.field_type = field_type;
9489        fact.names_function_type = names_function_type;
9490        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
9491        let captured = crate::source_context::capture_declaration_context(
9492            context,
9493            unit,
9494            self.source,
9495            &mut self.preprocessor_context_cache,
9496        );
9497        fact.guard_requirements = captured.guard_requirements;
9498        fact.callable_guards = fact.guard_requirements.clone();
9499        fact.callable_activation = Some(range.end_byte);
9500        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
9501        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
9502        let mut intern = |node: Node<'_>| {
9503            let mut collector = self.source_collector.borrow_mut();
9504            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
9505                collector.intern_node(node)
9506            } else {
9507                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
9508            }
9509        };
9510        fact.conditional_family =
9511            crate::graph::resolver::preprocessor_conditional_family_node(context).map(&mut intern);
9512        fact.exhaustive_conditional_family =
9513            captured.exhaustive_conditional_family.map(&mut intern);
9514        fact.displaced_namespace_closing_brace =
9515            captured.displaced_namespace_closing_brace.map(&mut intern);
9516        self.capture_source_dependencies(&mut fact, context, unit);
9517        self.apply_active_source_context(&mut fact);
9518        assert!(
9519            self.source_properties
9520                .borrow_mut()
9521                .insert(declaration, fact)
9522                .is_none()
9523        );
9524    }
9525
9526    fn materialize_object_macro_fields(
9527        &mut self,
9528        fields: Vec<MacroReplacementField>,
9529        range: Range,
9530        context: Node<'_>,
9531        scope: &ScopeInfo,
9532    ) {
9533        let Some(owner) = scope.class_unit.as_ref() else {
9534            return;
9535        };
9536        for field in fields {
9537            let signature = field.declaration.clone();
9538            let mut fq = owner.fq().clone();
9539            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9540            let short_name = if owner.short_name().is_empty() {
9541                field.name.clone()
9542            } else {
9543                format!("{}.{}", owner.short_name(), field.name)
9544            };
9545            let code_unit = CodeUnit::with_signature_and_fq(
9546                self.file.clone(),
9547                CodeUnitType::Field,
9548                owner.package_name().to_string(),
9549                short_name,
9550                Some(field.declaration),
9551                true,
9552                fq,
9553            );
9554            if self.parsed.contains_declaration(&code_unit) {
9555                continue;
9556            }
9557            self.add_declaration_with_range(
9558                self.primary_ancestry,
9559                code_unit.clone(),
9560                range,
9561                None,
9562                Some(owner.clone()),
9563                None,
9564            );
9565            self.capture_recovered_field_facts(
9566                &code_unit,
9567                range,
9568                context,
9569                field.field_type,
9570                field.names_function_type,
9571            );
9572            self.parsed.add_signature(code_unit, signature);
9573        }
9574    }
9575
9576    fn collapsed_aggregate_owner(
9577        &self,
9578        name: &str,
9579        scope: &ScopeInfo,
9580        parent: Option<&CodeUnit>,
9581    ) -> CodeUnit {
9582        let short_name = parent.map_or_else(
9583            || name.to_string(),
9584            |parent| cpp_join_nested_short(parent.short_name(), name),
9585        );
9586        let fq = cpp_leaf_fq(
9587            &scope.package_name,
9588            parent,
9589            name,
9590            SegmentKind::Nested,
9591            SegmentKind::Type,
9592        );
9593        CodeUnit::with_signature_and_fq(
9594            self.file.clone(),
9595            CodeUnitType::Class,
9596            scope.package_name.clone(),
9597            short_name,
9598            None,
9599            false,
9600            fq,
9601        )
9602    }
9603
9604    /// Recover ordinary aggregates whose field-list macro invocations made
9605    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
9606    /// The class markers, aggregate braces, field nodes, and macro identifier
9607    /// nodes are all read from the AST; no source delimiter or token scan is
9608    /// used. A close-brace node inside a malformed field closes the innermost
9609    /// recovered aggregate, preserving nested-owner identity.
9610    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9611        let mut cursor = node.walk();
9612        let children = node.children(&mut cursor).collect::<Vec<_>>();
9613        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
9614        let mut object_macro_fields = self.object_macro_fields.clone();
9615        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
9616        let mut open = Vec::<usize>::new();
9617        let mut index = 0;
9618        while index < children.len() {
9619            let keyword = children[index];
9620            if update_object_macro_field_environment(
9621                keyword,
9622                self.source,
9623                &mut object_macro_fields,
9624                &mut ambiguous_object_macro_fields,
9625            ) {
9626                index += 1;
9627                continue;
9628            }
9629            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
9630                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9631                if !name.is_empty() {
9632                    let parent = open
9633                        .last()
9634                        .and_then(|class| recovered.get(*class))
9635                        .map(|(owner, _, _, _)| owner.clone())
9636                        .or_else(|| scope.class_unit.clone());
9637                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
9638                    recovered.push((
9639                        owner,
9640                        head.key.start_byte(),
9641                        head.opening.end_byte(),
9642                        Vec::new(),
9643                    ));
9644                    let class_index = recovered.len() - 1;
9645                    match head.folded_members {
9646                        // The ordinary shape leaves the aggregate open: every
9647                        // child that follows belongs to it until a close brace
9648                        // ends it.
9649                        None => open.push(class_index),
9650                        // A folded head carries its own member list and its own
9651                        // closing brace, so it closes here. The invocations that
9652                        // precede the class key are the dangling tail of the
9653                        // replacement that folded it, and belong to whatever
9654                        // aggregate is open around it, not to this one.
9655                        Some(members) => {
9656                            let macro_nodes = object_macro_identifier_nodes_with_environment(
9657                                children[index],
9658                                self.source,
9659                                &mut object_macro_fields,
9660                                &mut ambiguous_object_macro_fields,
9661                            );
9662                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9663                                .iter()
9664                                .partition(|node| node.start_byte() < head.key.start_byte());
9665                            if let Some(&enclosing) = open.last() {
9666                                for macro_node in preceding {
9667                                    recovered[enclosing]
9668                                        .3
9669                                        .push(CppCollapsedMember::MacroFields {
9670                                            context: macro_node,
9671                                            range: cpp_declaration_range(macro_node),
9672                                            fields: object_macro_field_closure(
9673                                                &object_macro_fields,
9674                                                &normalize_cpp_whitespace(node_text(
9675                                                    macro_node,
9676                                                    self.source,
9677                                                )),
9678                                            ),
9679                                        });
9680                                }
9681                            }
9682                            let closing = cpp_collapsed_aggregate_closing_brace(members);
9683                            recovered[class_index]
9684                                .3
9685                                .extend(cpp_collapsed_aggregate_members(
9686                                    members,
9687                                    &inner,
9688                                    head.opening.end_byte()..closing,
9689                                    head.opening.end_position().row + 1,
9690                                    self.source,
9691                                    &object_macro_fields,
9692                                ));
9693                            recovered[class_index].2 = members.end_byte();
9694                            for &open_class in &open {
9695                                recovered[open_class].2 =
9696                                    recovered[open_class].2.max(members.end_byte());
9697                            }
9698                        }
9699                    }
9700                    index += head.width;
9701                    continue;
9702                }
9703            }
9704            if let Some(&class_index) = open.last()
9705                && children[index].kind() == "field_declaration"
9706            {
9707                let field = children[index];
9708                let macro_nodes = object_macro_identifier_nodes_with_environment(
9709                    field,
9710                    self.source,
9711                    &mut object_macro_fields,
9712                    &mut ambiguous_object_macro_fields,
9713                );
9714                recovered[class_index]
9715                    .3
9716                    .extend(cpp_collapsed_aggregate_members(
9717                        field,
9718                        &macro_nodes,
9719                        field.start_byte()..field.end_byte(),
9720                        field.start_position().row + 1,
9721                        self.source,
9722                        &object_macro_fields,
9723                    ));
9724                let end = field.end_byte();
9725                for &open_class in &open {
9726                    recovered[open_class].2 = recovered[open_class].2.max(end);
9727                }
9728                let closes = count_close_brace_nodes(field);
9729                for _ in 0..closes {
9730                    if let Some(closed) = open.pop() {
9731                        recovered[closed].2 = end;
9732                    }
9733                }
9734            }
9735            index += 1;
9736        }
9737
9738        let mut owners = Vec::with_capacity(recovered.len());
9739        for (owner, start, end, members) in recovered {
9740            let parent = owners
9741                .iter()
9742                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
9743                .cloned()
9744                .or_else(|| scope.class_unit.clone());
9745            self.declare_collapsed_aggregate(
9746                owner.clone(),
9747                start..end,
9748                Some(node),
9749                parent,
9750                members,
9751                scope,
9752            );
9753            owners.push(owner);
9754        }
9755    }
9756
9757    /// Record one recovered aggregate and the members it declares.
9758    fn declare_collapsed_aggregate(
9759        &mut self,
9760        owner: CodeUnit,
9761        span: std::ops::Range<usize>,
9762        evidence: Option<Node<'_>>,
9763        parent: Option<CodeUnit>,
9764        members: Vec<CppCollapsedMember<'_>>,
9765        scope: &ScopeInfo,
9766    ) {
9767        let range = Range {
9768            start_byte: span.start,
9769            end_byte: span.end,
9770            start_line: self.source.get(..span.start).map_or(1, |source| {
9771                source.bytes().filter(|byte| *byte == b'\n').count() + 1
9772            }),
9773            end_line: self.source.get(..span.end).map_or(1, |source| {
9774                source.bytes().filter(|byte| *byte == b'\n').count() + 1
9775            }),
9776        };
9777        // The tag can already be declared by a typedef or a forward
9778        // declaration, and this range is the definition's. Record it either
9779        // way: a declaration carries every range it is written at, and
9780        // dropping this one leaves nothing able to prove that the aggregate
9781        // has a body (#3098). Recording a range that is already held, or a
9782        // unit that is already declared, changes nothing.
9783        self.add_declaration_with_range(
9784            self.primary_ancestry,
9785            owner.clone(),
9786            range,
9787            evidence,
9788            parent,
9789            None,
9790        );
9791        // The recovery admitted this owner from an aggregate head and body.
9792        // Its malformed outer node need not itself be a struct_specifier, so
9793        // retain that structured definition proof explicitly.
9794        let declaration = *self.source_links[&owner]
9795            .last()
9796            .expect("collapsed aggregate has a source occurrence");
9797        {
9798            let mut properties = self.source_properties.borrow_mut();
9799            let fact = properties
9800                .get_mut(&declaration)
9801                .expect("collapsed aggregate retains its original context");
9802            fact.class_strength =
9803                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
9804            fact.occurrence_role =
9805                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
9806        }
9807        let owner_scope = ScopeInfo {
9808            class_unit: Some(owner),
9809            declarations_are_fields: true,
9810            ..scope.clone()
9811        };
9812        for member in members {
9813            match member {
9814                CppCollapsedMember::MacroFields {
9815                    range,
9816                    fields,
9817                    context,
9818                } => {
9819                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
9820                }
9821                CppCollapsedMember::Declarations {
9822                    span,
9823                    start_line,
9824                    context,
9825                } => {
9826                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
9827                }
9828            }
9829        }
9830    }
9831
9832    /// Recover an aggregate whose head tree-sitter folded into the declaration
9833    /// that precedes it, outside any collapsed `ERROR` container.
9834    ///
9835    /// A comment inside a field-list macro's replacement ends the replacement
9836    /// token, so the rest of the replacement is read at file scope. When the
9837    /// aggregate that follows declares its member list with one invocation,
9838    /// the parser reads the dangling tail as that declaration's type, the
9839    /// class key as an `ERROR`, and the member list as an initializer (#3098).
9840    /// The invocations before the class key belong to the replacement, not to
9841    /// this aggregate, so they go to whatever aggregate encloses it.
9842    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
9843        if node.parent().is_some_and(|parent| parent.is_error()) {
9844            // The container's own recovery reads this child in source order,
9845            // with the preprocessor environment its earlier children build.
9846            return false;
9847        }
9848        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
9849            return false;
9850        };
9851        let members = head
9852            .folded_members
9853            .expect("a folded aggregate head carries its member list");
9854        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
9855        if name.is_empty() {
9856            return false;
9857        }
9858        let macro_nodes =
9859            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
9860        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
9861            .iter()
9862            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
9863        for macro_node in preceding {
9864            let fields = object_macro_field_closure(
9865                &self.object_macro_fields,
9866                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
9867            );
9868            self.materialize_object_macro_fields(
9869                fields,
9870                cpp_declaration_range(macro_node),
9871                macro_node,
9872                scope,
9873            );
9874        }
9875        let parent = scope.class_unit.clone();
9876        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
9877        let recovered = cpp_collapsed_aggregate_members(
9878            members,
9879            &inner,
9880            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
9881            head.opening.end_position().row + 1,
9882            self.source,
9883            &self.object_macro_fields,
9884        );
9885        self.declare_collapsed_aggregate(
9886            owner,
9887            head.key.start_byte()..members.end_byte(),
9888            Some(node),
9889            parent,
9890            recovered,
9891            scope,
9892        );
9893        true
9894    }
9895
9896    /// Record the members of a collapsed aggregate region as fields of its
9897    /// recovered owner. The names, declaration text, and byte ranges all come
9898    /// from the grammar's reading of the region, so a region that is not a
9899    /// member list contributes nothing.
9900    fn record_collapsed_aggregate_fields(
9901        &mut self,
9902        span: std::ops::Range<usize>,
9903        start_line: usize,
9904        context: Node<'_>,
9905        scope: &ScopeInfo,
9906    ) {
9907        let Some(owner) = scope.class_unit.as_ref() else {
9908            return;
9909        };
9910        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
9911            let range = Range {
9912                start_byte: field.range.start,
9913                end_byte: field.range.end,
9914                start_line: start_line
9915                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
9916                end_line: start_line
9917                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
9918            };
9919            let mut fq = owner.fq().clone();
9920            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
9921            let short_name = if owner.short_name().is_empty() {
9922                field.name.clone()
9923            } else {
9924                format!("{}.{}", owner.short_name(), field.name)
9925            };
9926            let signature = normalize_cpp_whitespace(&field.declaration);
9927            let code_unit = CodeUnit::with_signature_and_fq(
9928                self.file.clone(),
9929                CodeUnitType::Field,
9930                owner.package_name().to_string(),
9931                short_name,
9932                Some(signature.clone()),
9933                false,
9934                fq,
9935            );
9936            if self.parsed.contains_declaration(&code_unit) {
9937                continue;
9938            }
9939            self.add_declaration_with_range(
9940                self.primary_ancestry,
9941                code_unit.clone(),
9942                range,
9943                None,
9944                Some(owner.clone()),
9945                None,
9946            );
9947            self.capture_recovered_field_facts(
9948                &code_unit,
9949                range,
9950                context,
9951                field.field_type,
9952                field.names_function_type,
9953            );
9954            self.parsed.add_signature(code_unit, signature);
9955        }
9956    }
9957
9958    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
9959        let Some(_directive) = node.child_by_field_name("directive") else {
9960            return;
9961        };
9962        if is_cpp_undef_directive(node, self.source) {
9963            update_object_macro_field_environment(
9964                node,
9965                self.source,
9966                &mut self.object_macro_fields,
9967                &mut self.ambiguous_object_macro_fields,
9968            );
9969            return;
9970        }
9971        let directly_in_field_list = node
9972            .parent()
9973            .is_some_and(|parent| parent.kind() == "field_declaration_list");
9974        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
9975            self.visit_object_macro_fields(node, scope);
9976        }
9977    }
9978}
9979
9980/// Every member a field-list macro contributes under `environment`, including
9981/// the members of the field-list macros it composes.
9982///
9983/// Composition is resolved at the invocation, not at the definition, because a
9984/// nested name's active replacement is a property of the environment the
9985/// invocation sees. The traversal is iterative and visits each name once, so a
9986/// self-composing or mutually composing pair terminates.
9987fn object_macro_field_closure(
9988    environment: &HashMap<String, ObjectMacroReplacement>,
9989    name: &str,
9990) -> Vec<MacroReplacementField> {
9991    let mut fields = Vec::new();
9992    let mut visited = HashSet::default();
9993    let mut stack = vec![name.to_string()];
9994    while let Some(current) = stack.pop() {
9995        if !visited.insert(current.clone()) {
9996            continue;
9997        }
9998        let Some(replacement) = environment.get(&current) else {
9999            continue;
10000        };
10001        fields.extend(replacement.fields.iter().cloned());
10002        stack.extend(replacement.nested.iter().rev().cloned());
10003    }
10004    fields
10005}
10006
10007/// The structured replacement an object-like `#define` contributes.
10008///
10009/// The directive's whole logical line is the replacement list, which
10010/// [`object_macro_replacement_span`] recovers, because a comment inside the
10011/// replacement ends tree-sitter's `preproc_arg` token early.
10012fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
10013    crate::graph::syntax::object_macro_replacement_span(node, source)
10014        .and_then(|span| source.get(span))
10015        .map(crate::graph::syntax::object_macro_replacement)
10016        .unwrap_or_default()
10017}
10018
10019/// Apply one preprocessor directive to an object-like field-list environment.
10020/// The environment is intentionally separate from the declaration visitor so
10021/// malformed parser regions can replay directives in source order without
10022/// mutating the live walk's state ahead of those directives.
10023fn update_object_macro_field_environment(
10024    node: Node<'_>,
10025    source: &str,
10026    fields: &mut HashMap<String, ObjectMacroReplacement>,
10027    ambiguous: &mut HashSet<String>,
10028) -> bool {
10029    match node.kind() {
10030        "preproc_def" => {
10031            let Some(name) = extract_macro_name(node, source) else {
10032                return false;
10033            };
10034            let replacement = object_macro_replacement_of(node, source);
10035            if replacement.is_empty() || ambiguous.contains(&name) {
10036                fields.remove(&name);
10037                ambiguous.insert(name);
10038            } else if let Some(previous) = fields.get(&name) {
10039                if previous != &replacement {
10040                    fields.remove(&name);
10041                    ambiguous.insert(name);
10042                }
10043            } else {
10044                fields.insert(name, replacement);
10045            }
10046            true
10047        }
10048        "preproc_call" if is_cpp_undef_directive(node, source) => {
10049            if let Some(argument) = node.child_by_field_name("argument") {
10050                let name = node_text(argument, source).trim();
10051                fields.remove(name);
10052                if inside_preprocessor_conditional(node) {
10053                    ambiguous.insert(name.to_string());
10054                } else {
10055                    ambiguous.remove(name);
10056                }
10057            }
10058            true
10059        }
10060        _ => false,
10061    }
10062}
10063
10064fn object_macro_identifier_nodes<'tree>(
10065    node: Node<'tree>,
10066    source: &str,
10067    fields: &HashMap<String, ObjectMacroReplacement>,
10068) -> Vec<Node<'tree>> {
10069    walk_object_macro_identifier_nodes(node, |current, result| {
10070        if matches!(
10071            current.kind(),
10072            "identifier" | "field_identifier" | "type_identifier"
10073        ) && fields.contains_key(node_text(current, source).trim())
10074        {
10075            result.push(current);
10076        }
10077        false
10078    })
10079}
10080
10081fn walk_object_macro_identifier_nodes<'tree>(
10082    node: Node<'tree>,
10083    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
10084) -> Vec<Node<'tree>> {
10085    let mut result = Vec::new();
10086    let mut stack = vec![node];
10087    while let Some(current) = stack.pop() {
10088        if visit(current, &mut result) {
10089            continue;
10090        }
10091        let mut cursor = current.walk();
10092        let children = current.children(&mut cursor).collect::<Vec<_>>();
10093        stack.extend(children.into_iter().rev());
10094    }
10095    result.sort_by_key(|node| node.start_byte());
10096    result
10097}
10098
10099/// Collect macro identifiers while replaying any nested preprocessor
10100/// directives in source order. Recovery regions can contain a later `#undef`
10101/// in the same parser error envelope; using the live visitor map for the
10102/// entire envelope would incorrectly materialize invocations after it.
10103fn object_macro_identifier_nodes_with_environment<'tree>(
10104    node: Node<'tree>,
10105    source: &str,
10106    fields: &mut HashMap<String, ObjectMacroReplacement>,
10107    ambiguous: &mut HashSet<String>,
10108) -> Vec<Node<'tree>> {
10109    walk_object_macro_identifier_nodes(node, |current, result| {
10110        if update_object_macro_field_environment(current, source, fields, ambiguous) {
10111            return true;
10112        }
10113        if matches!(
10114            current.kind(),
10115            "identifier" | "field_identifier" | "type_identifier"
10116        ) && fields.contains_key(node_text(current, source).trim())
10117        {
10118            result.push(current);
10119        }
10120        false
10121    })
10122}
10123
10124/// One member region of an aggregate whose head and body tree-sitter collapsed
10125/// into an `ERROR` container, in source order.
10126enum CppCollapsedMember<'tree> {
10127    /// The members a field-list macro invocation donates, all sharing the
10128    /// invocation's range.
10129    MacroFields {
10130        context: Node<'tree>,
10131        range: Range,
10132        fields: Vec<MacroReplacementField>,
10133    },
10134    /// A byte span whose ordinary member declarations lost their shape with
10135    /// the aggregate body, with the 1-based line the span starts on.
10136    Declarations {
10137        context: Node<'tree>,
10138        span: std::ops::Range<usize>,
10139        start_line: usize,
10140    },
10141}
10142
10143/// One aggregate head recovered from the children of a collapsed `ERROR`
10144/// container.
10145///
10146/// A member list that opens with an object-like field-list macro invocation
10147/// has no grammar rule, so the aggregate never forms a specifier and its head
10148/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
10149/// class key, the name, and the opening brace are three consecutive children.
10150/// When the aggregate follows the dangling tail of a comment-truncated
10151/// replacement, the parser reads that tail as the declaration's type and folds
10152/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
10153/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
10154/// }` that follows becomes one `field_declaration` whose class key is an
10155/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
10156/// `initializer_list` (#3098).
10157struct CppCollapsedAggregateHead<'tree> {
10158    key: Node<'tree>,
10159    name: Node<'tree>,
10160    opening: Node<'tree>,
10161    /// The member list when the head folded it into its own node, which also
10162    /// carries the aggregate's closing brace. `None` leaves the aggregate open
10163    /// for the children that follow it.
10164    folded_members: Option<Node<'tree>>,
10165    /// How many children the head consumes.
10166    width: usize,
10167}
10168
10169/// The aggregate head that starts at `children[index]`, if one does.
10170fn cpp_collapsed_aggregate_head<'tree>(
10171    children: &[Node<'tree>],
10172    index: usize,
10173    source: &str,
10174) -> Option<CppCollapsedAggregateHead<'tree>> {
10175    let key = *children.get(index)?;
10176    if matches!(key.kind(), "struct" | "class" | "union") {
10177        let name = *children.get(index + 1)?;
10178        let opening = *children.get(index + 2)?;
10179        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
10180            return None;
10181        }
10182        return Some(CppCollapsedAggregateHead {
10183            key,
10184            name,
10185            opening,
10186            folded_members: None,
10187            width: 3,
10188        });
10189    }
10190    cpp_folded_aggregate_head(key, source)
10191}
10192
10193/// The aggregate head tree-sitter folded into one declaration node, reading
10194/// the class key as an `ERROR` and the member list as an initializer.
10195fn cpp_folded_aggregate_head<'tree>(
10196    node: Node<'tree>,
10197    source: &str,
10198) -> Option<CppCollapsedAggregateHead<'tree>> {
10199    if !matches!(node.kind(), "declaration" | "field_declaration") {
10200        return None;
10201    }
10202    let mut cursor = node.walk();
10203    let children = node.children(&mut cursor).collect::<Vec<_>>();
10204    let key_index = children.iter().position(|child| {
10205        child.is_error()
10206            && matches!(
10207                node_text(*child, source).trim(),
10208                "struct" | "class" | "union"
10209            )
10210    })?;
10211    let declarator = *children.get(key_index + 1)?;
10212    // At file scope the grammar has an `init_declarator` for the name and its
10213    // initializer; inside an `ERROR` container the two are loose siblings.
10214    let (name, members) = if declarator.kind() == "init_declarator" {
10215        (
10216            declarator.child_by_field_name("declarator")?,
10217            declarator.child_by_field_name("value")?,
10218        )
10219    } else {
10220        (declarator, *children.get(key_index + 2)?)
10221    };
10222    if !matches!(
10223        name.kind(),
10224        "field_identifier" | "type_identifier" | "identifier"
10225    ) {
10226        return None;
10227    }
10228    if members.kind() != "initializer_list" {
10229        return None;
10230    }
10231    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
10232    Some(CppCollapsedAggregateHead {
10233        key: children[key_index],
10234        name,
10235        opening,
10236        folded_members: Some(members),
10237        width: 1,
10238    })
10239}
10240
10241/// Where a folded member list ends: at its own closing brace when it has one,
10242/// so the reparse of the declarations it holds sees a member list and not a
10243/// stray brace.
10244fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
10245    let mut cursor = members.walk();
10246    members
10247        .children(&mut cursor)
10248        .filter(|child| child.kind() == "}" && !child.is_missing())
10249        .last()
10250        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
10251}
10252
10253/// The members one collapsed aggregate region declares, in source order: what
10254/// its field-list macro invocations donate, then the ordinary declarations
10255/// that follow the last invocation and lost their shape with the body.
10256fn cpp_collapsed_aggregate_members<'tree>(
10257    context: Node<'tree>,
10258    macro_nodes: &[Node<'tree>],
10259    region: std::ops::Range<usize>,
10260    region_start_line: usize,
10261    source: &str,
10262    environment: &HashMap<String, ObjectMacroReplacement>,
10263) -> Vec<CppCollapsedMember<'tree>> {
10264    let mut members = macro_nodes
10265        .iter()
10266        .map(|macro_node| CppCollapsedMember::MacroFields {
10267            context: *macro_node,
10268            range: cpp_declaration_range(*macro_node),
10269            fields: object_macro_field_closure(
10270                environment,
10271                &normalize_cpp_whitespace(node_text(*macro_node, source)),
10272            ),
10273        })
10274        .collect::<Vec<_>>();
10275    // Whatever follows the last invocation is an ordinary member list that
10276    // lost its declaration shape with the aggregate's body; the grammar
10277    // recovers it from its own byte range.
10278    let declarations_start = macro_nodes
10279        .last()
10280        .map_or(region.start, |macro_node| macro_node.end_byte());
10281    members.push(CppCollapsedMember::Declarations {
10282        context,
10283        span: declarations_start..region.end,
10284        start_line: region_start_line
10285            + cpp_line_breaks_between(source, region.start, declarations_start),
10286    });
10287    members
10288}
10289
10290/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
10291/// line without rescanning the file from its start.
10292fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
10293    source.get(from..to).map_or(0, |slice| {
10294        slice.bytes().filter(|byte| *byte == b'\n').count()
10295    })
10296}
10297
10298/// How many enclosing aggregates a collapsed member declaration closes: the
10299/// close braces it carries that its own braces do not balance. A member that
10300/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
10301/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
10302/// owner early and lost every member after it (#2985).
10303fn count_close_brace_nodes(node: Node<'_>) -> usize {
10304    let mut opened = 0usize;
10305    let mut closed = 0usize;
10306    let mut stack = vec![node];
10307    while let Some(current) = stack.pop() {
10308        if !current.is_missing() {
10309            match current.kind() {
10310                "{" => opened += 1,
10311                "}" => closed += 1,
10312                _ => {}
10313            }
10314        }
10315        let mut cursor = current.walk();
10316        stack.extend(current.children(&mut cursor));
10317    }
10318    closed.saturating_sub(opened)
10319}
10320
10321/// Classify a C++ field while its declaration syntax is already available.
10322///
10323/// The persisted result lets later visibility queries avoid reparsing the
10324/// complete source file only to recover linkage.
10325pub fn cpp_field_declaration_linkage<'tree>(
10326    declaration: Node<'tree>,
10327    source: &str,
10328    ancestry: &ParentIndex<'tree>,
10329) -> CppFieldLinkage {
10330    let mut current = ancestry.parent(declaration);
10331    let mut enclosed_by_class = false;
10332    while let Some(node) = current {
10333        if node.kind() == "namespace_definition"
10334            && node
10335                .child_by_field_name("name")
10336                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10337        {
10338            return CppFieldLinkage::Internal;
10339        }
10340        if matches!(
10341            node.kind(),
10342            "class_specifier" | "struct_specifier" | "union_specifier"
10343        ) && node
10344            .child_by_field_name("name")
10345            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10346        {
10347            return CppFieldLinkage::Internal;
10348        }
10349        if matches!(
10350            node.kind(),
10351            "class_specifier" | "struct_specifier" | "union_specifier"
10352        ) {
10353            enclosed_by_class = true;
10354        }
10355        if matches!(node.kind(), "function_definition" | "lambda_expression") {
10356            return CppFieldLinkage::Internal;
10357        }
10358        current = ancestry.parent(node);
10359    }
10360    if enclosed_by_class {
10361        return CppFieldLinkage::External;
10362    }
10363    let mut cursor = declaration.walk();
10364    let mut has_static = false;
10365    let mut has_extern = false;
10366    let mut has_inline = false;
10367    let mut has_const = false;
10368    let mut has_constexpr = false;
10369    for child in declaration.named_children(&mut cursor) {
10370        let text = normalize_cpp_whitespace(node_text(child, source));
10371        match (child.kind(), text.as_str()) {
10372            ("storage_class_specifier", "static") => has_static = true,
10373            ("storage_class_specifier", "extern") => has_extern = true,
10374            ("storage_class_specifier", "inline") => has_inline = true,
10375            ("storage_class_specifier", "constexpr") => has_constexpr = true,
10376            ("type_qualifier", "const") => has_const = true,
10377            ("type_qualifier", "constexpr") => has_constexpr = true,
10378            _ => {}
10379        }
10380    }
10381    if has_static {
10382        CppFieldLinkage::Internal
10383    } else if has_extern || has_inline {
10384        CppFieldLinkage::External
10385    } else if has_const || has_constexpr {
10386        CppFieldLinkage::InternalUnlessExternalPeer
10387    } else {
10388        CppFieldLinkage::External
10389    }
10390}
10391
10392fn cpp_declaration_range(node: Node<'_>) -> Range {
10393    Range {
10394        start_byte: node.start_byte(),
10395        end_byte: node.end_byte(),
10396        start_line: node.start_position().row + 1,
10397        end_line: node.end_position().row + 1,
10398    }
10399}
10400
10401/// A recovery interval as a [`Range`], for materialization records whose
10402/// window is a byte region rather than one parser node (the sentinel-macro
10403/// region reparses, issue #941/#1657).
10404fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
10405    let line_at = |byte: usize| {
10406        source.as_bytes()[..byte]
10407            .iter()
10408            .filter(|&&b| b == b'\n')
10409            .count()
10410            + 1
10411    };
10412    Range {
10413        start_byte,
10414        end_byte,
10415        start_line: line_at(start_byte),
10416        end_line: line_at(end_byte),
10417    }
10418}
10419
10420fn cpp_include_info(raw_snippet: String) -> ImportInfo {
10421    ImportInfo {
10422        raw_snippet,
10423        is_wildcard: false,
10424        is_global: false,
10425        identifier: None,
10426        alias: None,
10427        path: None,
10428        binder_span: None,
10429    }
10430}
10431
10432fn recover_quoted_source_imports(
10433    source: &str,
10434    imports: &mut Vec<SourceImportFact>,
10435    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
10436    collector: &RefCell<PrimarySourceFactCollector<'_>>,
10437) {
10438    let mut in_block_comment = false;
10439    let mut start = 0;
10440    for line in source.split_inclusive('\n') {
10441        let end = start + line.len();
10442        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
10443        let trimmed = stripped.trim();
10444        if looks_like_quoted_include_line(trimmed) {
10445            let raw = normalize_cpp_whitespace(trimmed);
10446            if !imports.iter().any(|import| import.statement == raw) {
10447                let occurrence = collector.borrow_mut().intern_subspan_bytes(
10448                    start,
10449                    end,
10450                    SourceOccurrenceProvenance::ExplicitSubspan,
10451                );
10452                // Recovery already identified a quoted directive hidden by a
10453                // malformed outer construct. Interpret its path with the same
10454                // grammar in an isolated included range, preserving byte identity.
10455                let region =
10456                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
10457                let target = region.as_ref().and_then(|tree| {
10458                    let mut pending = vec![tree.root_node()];
10459                    while let Some(node) = pending.pop() {
10460                        if node.kind() == "preproc_include"
10461                            && let Some(path) = node.child_by_field_name("path")
10462                            && path.kind() == "string_literal"
10463                        {
10464                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
10465                            let target = collector.borrow_mut().intern_embedded(
10466                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
10467                            );
10468                            includes.push(
10469                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
10470                                    declaration: occurrence,
10471                                    target,
10472                                    path: value.to_owned(),
10473                                    quoted: true,
10474                                },
10475                            );
10476                            return Some(target);
10477                        }
10478                        let mut cursor = node.walk();
10479                        let children: Vec<_> = node.named_children(&mut cursor).collect();
10480                        pending.extend(children.into_iter().rev());
10481                    }
10482                    None
10483                });
10484                imports.push(SourceImportFact::from_import(
10485                    cpp_include_info(raw),
10486                    occurrence,
10487                    target,
10488                    None,
10489                    Vec::new(),
10490                ));
10491            }
10492        }
10493        start = end;
10494    }
10495}
10496
10497fn looks_like_quoted_include_line(line: &str) -> bool {
10498    let Some(rest) = line.trim_start().strip_prefix('#') else {
10499        return false;
10500    };
10501    let Some(rest) = rest.trim_start().strip_prefix("include") else {
10502        return false;
10503    };
10504    rest.trim_start().starts_with('"')
10505}
10506
10507fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
10508    let mut raw = Vec::new();
10509    if let Some(bases) = cpp_class_like_base_clause(node) {
10510        collect_cpp_base_nodes(bases, source, &mut raw);
10511    }
10512    raw
10513}
10514
10515fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
10516    let mut facts = Vec::new();
10517    let mut cursor = node.walk();
10518    for child in node.named_children(&mut cursor) {
10519        if child.kind() != "base_class_clause" {
10520            continue;
10521        }
10522        let mut is_virtual = false;
10523        let mut child_cursor = child.walk();
10524        for base in child.children(&mut child_cursor) {
10525            match base.kind() {
10526                "virtual" => is_virtual = true,
10527                "," => is_virtual = false,
10528                "type_identifier"
10529                | "qualified_identifier"
10530                | "scoped_type_identifier"
10531                | "template_type"
10532                | "template_function" => {
10533                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
10534                        base,
10535                        source,
10536                        is_virtual,
10537                        crate::source_facts::base_access_specifier(child, base, node.kind()),
10538                    ) {
10539                        facts.push(fact);
10540                    }
10541                    is_virtual = false;
10542                }
10543                _ => {}
10544            }
10545        }
10546    }
10547    facts
10548}
10549
10550/// The `base_class_clause` a class-like node writes, if it writes one.
10551///
10552/// The pinned grammar exposes the clause as a named child rather than a field,
10553/// so the question is asked by kind. A base clause is the only place a C++
10554/// class body records that some other class may declare a member, which is
10555/// what [`cpp_callable_dispatch_extensibility`] reads it for.
10556fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
10557    let mut cursor = class_like.walk();
10558    class_like
10559        .named_children(&mut cursor)
10560        .find(|child| child.kind() == "base_class_clause")
10561}
10562
10563fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
10564    walk_named_tree_preorder(node, false, |child| match child.kind() {
10565        "type_identifier" | "qualified_identifier" | "template_type" => {
10566            let text = normalize_cpp_whitespace(node_text(child, source));
10567            if !text.is_empty() {
10568                raw.push(text);
10569            }
10570            WalkControl::SkipChildren
10571        }
10572        _ => WalkControl::Continue,
10573    });
10574}
10575
10576fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
10577    let mut out = String::new();
10578    let chars: Vec<char> = line.chars().collect();
10579    let mut index = 0;
10580    let mut in_string = false;
10581    let mut in_char = false;
10582    let mut escape = false;
10583
10584    while index < chars.len() {
10585        let ch = chars[index];
10586        let next = chars.get(index + 1).copied();
10587
10588        if *in_block_comment {
10589            if ch == '*' && next == Some('/') {
10590                *in_block_comment = false;
10591                index += 2;
10592            } else {
10593                index += 1;
10594            }
10595            continue;
10596        }
10597
10598        if in_string {
10599            out.push(ch);
10600            if escape {
10601                escape = false;
10602            } else if ch == '\\' {
10603                escape = true;
10604            } else if ch == '"' {
10605                in_string = false;
10606            }
10607            index += 1;
10608            continue;
10609        }
10610
10611        if in_char {
10612            out.push(ch);
10613            if escape {
10614                escape = false;
10615            } else if ch == '\\' {
10616                escape = true;
10617            } else if ch == '\'' {
10618                in_char = false;
10619            }
10620            index += 1;
10621            continue;
10622        }
10623
10624        if ch == '/' && next == Some('/') {
10625            break;
10626        }
10627        if ch == '/' && next == Some('*') {
10628            *in_block_comment = true;
10629            index += 2;
10630            continue;
10631        }
10632        if ch == '"' {
10633            in_string = true;
10634            out.push(ch);
10635            index += 1;
10636            continue;
10637        }
10638        if ch == '\'' {
10639            in_char = true;
10640            out.push(ch);
10641            index += 1;
10642            continue;
10643        }
10644
10645        out.push(ch);
10646        index += 1;
10647    }
10648
10649    out
10650}
10651
10652#[derive(Clone)]
10653struct FunctionInfo {
10654    package_name: String,
10655    owner: Option<CppMemberOwner>,
10656    name: String,
10657    signature: String,
10658}
10659
10660/// Owner of a member function, kept structured so a literal `$` inside a
10661/// source-level class name never crosses a join/split boundary: the legacy
10662/// `$`-joined owner string was re-split at fq construction, dropping a leading
10663/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
10664/// and tripping the package/short boundary assert -- the #2140 corruption one
10665/// level up (#2362).
10666#[derive(Clone)]
10667enum CppMemberOwner {
10668    /// Source-level owner class chain from a qualified declarator-id, one
10669    /// class name per component (`Outer::Inner::method` -> `["Outer",
10670    /// "Inner"]`); each component may itself contain a literal `$`.
10671    Chain(Vec<String>),
10672    /// The lexically enclosing or recovered class unit; the member fq extends
10673    /// its fq directly instead of re-splitting its `$`-joined short chain.
10674    Unit(CodeUnit),
10675}
10676
10677impl CppMemberOwner {
10678    /// The legacy `$`-joined owner chain used in the member's short name.
10679    fn short_chain(&self) -> String {
10680        match self {
10681            Self::Chain(chain) => chain.join("$"),
10682            Self::Unit(parent) => parent.short_name().to_string(),
10683        }
10684    }
10685}
10686
10687enum DeclaratorKind<'a> {
10688    Function(Node<'a>),
10689    Variable(Node<'a>),
10690}
10691
10692impl FunctionInfo {
10693    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
10694        self.code_unit_with_synthetic(file, false)
10695    }
10696
10697    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
10698        let short_name = match &self.owner {
10699            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
10700            None => self.name.clone(),
10701        };
10702        let fq = match &self.owner {
10703            Some(CppMemberOwner::Chain(chain)) => {
10704                debug_assert!(
10705                    !chain.is_empty(),
10706                    "an empty owner chain is no owner; producers return None instead"
10707                );
10708                let mut fq = FqName::new();
10709                cpp_push_package(&mut fq, &self.package_name);
10710                let mut first = true;
10711                for component in chain {
10712                    let kind = if first {
10713                        SegmentKind::Type
10714                    } else {
10715                        SegmentKind::Nested
10716                    };
10717                    fq.push(segment_interner().intern(component, kind));
10718                    first = false;
10719                }
10720                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
10721                fq
10722            }
10723            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
10724                .fq()
10725                .clone()
10726                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
10727            // An anonymous parent (empty short chain) contributes no owner
10728            // segment -- the same guard as cpp_join_member_short above.
10729            Some(CppMemberOwner::Unit(_)) | None => {
10730                let mut fq = FqName::new();
10731                cpp_push_package(&mut fq, &self.package_name);
10732                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
10733                fq
10734            }
10735        };
10736        CodeUnit::with_signature_and_fq(
10737            file,
10738            CodeUnitType::Function,
10739            self.package_name.clone(),
10740            short_name,
10741            Some(self.signature.clone()),
10742            synthetic,
10743            fq,
10744        )
10745    }
10746}
10747
10748fn extract_function_info(
10749    declarator: Node<'_>,
10750    source: &str,
10751    scope: &ScopeInfo,
10752) -> Option<FunctionInfo> {
10753    let parameters_node = declarator.child_by_field_name("parameters")?;
10754    let declarator_name_node = declarator
10755        .child_by_field_name("declarator")
10756        .or_else(|| parameters_node.prev_named_sibling())?;
10757    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
10758}
10759
10760fn extract_function_info_from_name(
10761    declarator: Node<'_>,
10762    declarator_name_node: Node<'_>,
10763    source: &str,
10764    scope: &ScopeInfo,
10765) -> Option<FunctionInfo> {
10766    let parameters_node = declarator.child_by_field_name("parameters")?;
10767    let parameters_text = cpp_parameter_signature(parameters_node, source);
10768    let recovered_specialization_member = scope
10769        .recovered_specialization_member_scope
10770        .then(|| {
10771            let terminal = declarator_name_node
10772                .child_by_field_name("name")
10773                .unwrap_or(declarator_name_node);
10774            let name = canonical_cpp_qualified_component(terminal, source)?.name;
10775            let owner = scope.class_unit.as_ref()?;
10776            Some((
10777                Some(CppMemberOwner::Unit(owner.clone())),
10778                name,
10779                scope.package_name.clone(),
10780            ))
10781        })
10782        .flatten();
10783    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
10784        parts
10785    } else if let Some(parts) =
10786        split_structured_templated_cpp_name(declarator_name_node, source, scope)
10787    {
10788        parts
10789    } else {
10790        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
10791            declarator_name_node,
10792            source,
10793        )?);
10794        if raw_name.is_empty() {
10795            return None;
10796        }
10797        split_cpp_name(&raw_name, scope)
10798    };
10799    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
10800    let mut signature = if suffix.is_empty() {
10801        parameters_text
10802    } else {
10803        format!("{parameters_text} {suffix}")
10804    };
10805    if let Some(template_signature) = &scope.template_signature {
10806        signature = format!("{template_signature}{signature}");
10807    }
10808
10809    Some(FunctionInfo {
10810        package_name,
10811        owner,
10812        name,
10813        signature,
10814    })
10815}
10816
10817/// Recover the semantic return type and callable name when a declaration macro
10818/// occupies a function definition's `type` field. Tree-sitter either exposes a
10819/// scalar return as the declarator's apparent name and the callable as the sole
10820/// identifier in an `ERROR`, or joins a template return and callable into a
10821/// qualified identifier with a missing `::`. Both shapes retain the complete
10822/// parameter list and body; a concrete separator remains an out-of-line member.
10823fn cpp_macro_displaced_callable_parts<'tree>(
10824    function_declarator: Node<'tree>,
10825    source: &str,
10826    ancestry: &ParentIndex<'tree>,
10827) -> Option<(Node<'tree>, Node<'tree>)> {
10828    let definition = ancestry.parent(function_declarator)?;
10829    if definition.kind() != "function_definition"
10830        || definition.child_by_field_name("declarator") != Some(function_declarator)
10831        || definition
10832            .child_by_field_name("body")
10833            .is_none_or(|body| body.kind() != "compound_statement")
10834    {
10835        return None;
10836    }
10837    let macro_type = definition.child_by_field_name("type")?;
10838    if macro_type.kind() != "type_identifier"
10839        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10840    {
10841        return None;
10842    }
10843
10844    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
10845    if apparent_return_type.kind() == "qualified_identifier"
10846        && let (Some(return_type), Some(callable_name)) = (
10847            apparent_return_type.child_by_field_name("scope"),
10848            apparent_return_type.child_by_field_name("name"),
10849        )
10850        && return_type.kind() == "template_type"
10851        && matches!(callable_name.kind(), "identifier" | "field_identifier")
10852        && (0..apparent_return_type.child_count())
10853            .filter_map(|index| apparent_return_type.child(index))
10854            .any(|child| child.kind() == "::" && child.is_missing())
10855        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
10856        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10857    {
10858        return Some((return_type, callable_name));
10859    }
10860    if !matches!(
10861        apparent_return_type.kind(),
10862        "identifier" | "field_identifier" | "type_identifier"
10863    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
10864    {
10865        return None;
10866    }
10867    let parameters = function_declarator.child_by_field_name("parameters")?;
10868    let mut cursor = function_declarator.walk();
10869    let between = function_declarator
10870        .named_children(&mut cursor)
10871        .filter(|child| child.kind() != "comment")
10872        .filter(|child| {
10873            child.start_byte() >= apparent_return_type.end_byte()
10874                && child.end_byte() <= parameters.start_byte()
10875                && !same_node(*child, apparent_return_type)
10876                && !same_node(*child, parameters)
10877        })
10878        .collect::<Vec<_>>();
10879    let [name_error] = between.as_slice() else {
10880        return None;
10881    };
10882    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
10883        return None;
10884    }
10885    let callable_name = name_error.named_child(0)?;
10886    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
10887        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
10888    {
10889        return None;
10890    }
10891    Some((apparent_return_type, callable_name))
10892}
10893
10894/// The part of a `function_declarator` after its parameter list that belongs to
10895/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
10896/// specification, a trailing return type and a trailing requires-clause.
10897///
10898/// The grammar makes each of these a distinct sibling of the `parameters`
10899/// field, so they are read from the tree. Splitting the declarator's text on
10900/// the parameter list instead silently dropped every qualifier whenever the
10901/// parameter list was spelled with whitespace that normalization rewrote - a
10902/// line break or a double space was enough to make a `const` member definition
10903/// a different logical symbol from its declaration (#1827).
10904///
10905/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
10906/// are deliberately excluded. C++ does not make them part of the signature and
10907/// an out-of-line definition never repeats them, so including them would split
10908/// a declaration from its own definition.
10909fn cpp_declarator_identity_suffix(
10910    declarator: Node<'_>,
10911    parameters_node: Node<'_>,
10912    source: &str,
10913) -> String {
10914    let mut cursor = declarator.walk();
10915    let parts = declarator
10916        .named_children(&mut cursor)
10917        .filter(|child| child.start_byte() >= parameters_node.end_byte())
10918        .filter(|child| {
10919            matches!(
10920                child.kind(),
10921                "type_qualifier"
10922                    | "ref_qualifier"
10923                    | "noexcept"
10924                    | "throw_specifier"
10925                    | "trailing_return_type"
10926                    | "requires_clause"
10927            )
10928        })
10929        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
10930        .filter(|text| !text.is_empty())
10931        .collect::<Vec<_>>();
10932    normalize_cpp_qualifier_suffix(&parts.join(" "))
10933}
10934
10935/// The identity suffix of one callable declarator, for a consumer that holds
10936/// the declarator rather than the declaration walk's parts.
10937///
10938/// The persisted signature concatenates the parameter spelling and this suffix,
10939/// so a comparison that must agree on the suffix alone recomputes it here
10940/// instead of splitting the stored string.
10941pub(crate) fn cpp_callable_identity_suffix(
10942    function_declarator: Node<'_>,
10943    source: &str,
10944) -> Option<String> {
10945    let parameters_node = function_declarator.child_by_field_name("parameters")?;
10946    Some(cpp_declarator_identity_suffix(
10947        function_declarator,
10948        parameters_node,
10949        source,
10950    ))
10951}
10952
10953pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
10954    match classify_declarator(node)? {
10955        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
10956        DeclaratorKind::Variable(_) => None,
10957    }
10958}
10959
10960fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
10961    match node.kind() {
10962        "function_declarator" => {
10963            let inner = node
10964                .child_by_field_name("declarator")
10965                .or_else(|| node.child_by_field_name("name"))
10966                .or_else(|| last_named_child(node));
10967            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
10968                Some(DeclaratorKind::Variable(node))
10969            } else {
10970                Some(DeclaratorKind::Function(node))
10971            }
10972        }
10973        "init_declarator"
10974        | "pointer_declarator"
10975        | "reference_declarator"
10976        | "parenthesized_declarator"
10977        | "array_declarator"
10978        | "attributed_declarator"
10979        | "template_function" => node
10980            .child_by_field_name("declarator")
10981            .or_else(|| node.child_by_field_name("name"))
10982            .or_else(|| last_named_child(node))
10983            .and_then(classify_declarator),
10984        "identifier" | "field_identifier" | "qualified_identifier" => {
10985            Some(DeclaratorKind::Variable(node))
10986        }
10987        _ => node
10988            .child_by_field_name("declarator")
10989            .or_else(|| node.child_by_field_name("name"))
10990            .or_else(|| last_named_child(node))
10991            .and_then(classify_declarator),
10992    }
10993}
10994
10995fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
10996    matches!(
10997        node.kind(),
10998        "function_declarator"
10999            | "init_declarator"
11000            | "pointer_declarator"
11001            | "reference_declarator"
11002            | "parenthesized_declarator"
11003            | "array_declarator"
11004            | "attributed_declarator"
11005            | "template_function"
11006            | "identifier"
11007            | "field_identifier"
11008            | "qualified_identifier"
11009    )
11010}
11011
11012fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
11013    let class_like = first_class_like_child(node);
11014    let mut cursor = node.walk();
11015    node.named_children(&mut cursor).any(|child| {
11016        matches!(
11017            child.kind(),
11018            "init_declarator"
11019                | "pointer_declarator"
11020                | "reference_declarator"
11021                | "array_declarator"
11022                | "function_declarator"
11023                | "parenthesized_declarator"
11024                | "attributed_declarator"
11025        ) || matches!(
11026            child.kind(),
11027            "identifier" | "field_identifier" | "qualified_identifier"
11028        ) && class_like.is_none_or(|class_node| {
11029            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
11030        })
11031    })
11032}
11033
11034/// One namespace-scope forward class declaration that a recovered export-macro
11035/// class definition may borrow its identity from.  Tree-sitter can close a
11036/// malformed class at the enclosing namespace's closing brace, leaving the later
11037/// class definitions as root-level recovered `function_definition` nodes.  A
11038/// preceding `class Name;` in the same namespace is the only structured identity
11039/// signal available in that shape.
11040///
11041/// Everything recorded here is a property of the forward declaration alone.
11042/// What depends on the node doing the asking -- that the forward and its
11043/// namespace both close before it, with nothing but recovery trivia between --
11044/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
11045/// tree answers every later question about it.
11046struct CppNamespaceForward {
11047    name: String,
11048    start_byte: usize,
11049    /// Where the malformed namespace that held the forward ended.  No query
11050    /// asks anything else about that node.
11051    namespace_end_byte: usize,
11052    package_name: String,
11053}
11054
11055/// Read `node` as a borrowable namespace forward declaration.
11056///
11057/// The admission is deliberately conservative: only a body-less class specifier
11058/// whose declaration has no declarator, at namespace scope rather than inside a
11059/// function or class body, in a namespace that itself failed to parse.
11060fn cpp_namespace_forward_entry<'tree>(
11061    node: Node<'tree>,
11062    source: &str,
11063    ancestry: &ParentIndex<'tree>,
11064) -> Option<CppNamespaceForward> {
11065    if !matches!(
11066        node.kind(),
11067        "class_specifier" | "struct_specifier" | "union_specifier"
11068    ) || cpp_body_node(node).is_some()
11069    {
11070        return None;
11071    }
11072    let parent = node.parent()?;
11073    if !(parent.kind() == "declaration_list"
11074        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
11075    {
11076        return None;
11077    }
11078    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
11079    // Borrowing is only justified by the parser-recovery shape we are
11080    // repairing: the namespace that held the forward must itself contain a
11081    // syntax error. A clean, unrelated namespace forward is not an identity
11082    // proof.
11083    if !namespace.has_error() {
11084        return None;
11085    }
11086    Some(CppNamespaceForward {
11087        name: class_like_name(node, source, ancestry)?,
11088        start_byte: node.start_byte(),
11089        namespace_end_byte: namespace.end_byte(),
11090        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
11091    })
11092}
11093
11094/// Whether `forward` stands in the recovery relation to the node asking about
11095/// it: it and its malformed namespace both closed before the recovered class,
11096/// and nothing but recovery trivia separates the two.
11097fn cpp_namespace_forward_matches_recovery(
11098    forward: &CppNamespaceForward,
11099    recovered_node: Node<'_>,
11100) -> bool {
11101    forward.start_byte < recovered_node.start_byte()
11102        && forward.namespace_end_byte < recovered_node.start_byte()
11103        && malformed_namespace_is_nearest_recovery_region(
11104            forward.namespace_end_byte,
11105            recovered_node,
11106        )
11107}
11108
11109/// What one open [`CppVisitor::record_recovered_declarations`] has watched
11110/// happen to the declaration set.
11111///
11112/// The recovered set used to be a difference against a clone of the whole
11113/// declaration set, taken once per recovery: O(recoveries x declarations) on
11114/// exactly the error-recovered files that already walk slowest (#2787). The
11115/// walk knows which declarations it creates, so the capture collects them as
11116/// they are made and the difference is never needed.
11117///
11118/// `removed_pre_existing` is what makes that equal to the difference. A
11119/// deferred replacement removes the replaced declaration's children
11120/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
11121/// re-creates them. Creation alone cannot tell that apart from a first mint, so
11122/// a removal of a declaration this capture did not create records that it was
11123/// already there when the capture opened.
11124#[derive(Debug, Default)]
11125pub struct CppRecoveryCapture {
11126    /// Declarations created while this capture was open, in creation order.
11127    created: Vec<CodeUnit>,
11128    /// Membership for `created`.
11129    created_units: HashSet<CodeUnit>,
11130    /// Declarations that predate this capture and have been removed during it.
11131    removed_pre_existing: HashSet<CodeUnit>,
11132}
11133
11134/// Which owners the parse product already holds field declarations for, folded
11135/// in as the walk records them.
11136///
11137/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
11138/// and used to answer it by scanning every declaration accumulated so far:
11139/// O(enums x declarations) on exactly the generated headers that declare many
11140/// of both (#2786). The answer only grows by declaration, so the walk carries
11141/// it. A field's short name names its owner chain, `Owner.member`, so one field
11142/// answers for every dotted prefix of its own short name; an anonymous enum's
11143/// or union's enumerators carry bare short names instead (#2140), which is what
11144/// an empty owner short name asks about.
11145#[derive(Debug, Default)]
11146pub struct CppFieldOwnerIndex {
11147    /// Package name -> the owner short names its fields name.
11148    owners: HashMap<String, HashSet<String>>,
11149    /// Packages holding at least one field that names no owner.
11150    ownerless_packages: HashSet<String>,
11151}
11152
11153impl CppFieldOwnerIndex {
11154    /// The index of the declarations recorded so far, built when the first
11155    /// question arrives.
11156    fn of<'unit>(
11157        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
11158        file: &ProjectFile,
11159    ) -> Self {
11160        let mut index = Self::default();
11161        for declaration in declarations {
11162            index.record(declaration, file);
11163        }
11164        index
11165    }
11166
11167    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
11168        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
11169            return;
11170        }
11171        let short_name = code_unit.short_name();
11172        let package_name = code_unit.package_name();
11173        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
11174            self.ownerless_packages.insert(package_name.to_string());
11175        }
11176        if !short_name.contains('.') {
11177            return;
11178        }
11179        if !self.owners.contains_key(package_name) {
11180            self.owners
11181                .insert(package_name.to_string(), HashSet::default());
11182        }
11183        let owners = self
11184            .owners
11185            .get_mut(package_name)
11186            .expect("the package entry was just ensured");
11187        for (offset, _) in short_name.match_indices('.') {
11188            let owner = &short_name[..offset];
11189            if !owners.contains(owner) {
11190                owners.insert(owner.to_string());
11191            }
11192        }
11193    }
11194
11195    /// Whether a field declaration in `package_name` names `owner_short_name`
11196    /// as its owner. An empty owner asks about ownerless fields instead.
11197    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
11198        if owner_short_name.is_empty() {
11199            self.ownerless_packages.contains(package_name)
11200        } else {
11201            self.owners
11202                .get(package_name)
11203                .is_some_and(|owners| owners.contains(owner_short_name))
11204        }
11205    }
11206}
11207
11208/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
11209/// debug build asserts every carried answer against and as the release-mode
11210/// parity tests' reference (#2786).
11211#[cfg(any(debug_assertions, test))]
11212fn cpp_declarations_hold_owned_fields<'unit>(
11213    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
11214    file: &ProjectFile,
11215    package_name: &str,
11216    owner_short_name: &str,
11217) -> bool {
11218    let prefix = format!("{owner_short_name}.");
11219    declarations.into_iter().any(|unit| {
11220        unit.kind() == CodeUnitType::Field
11221            && unit.source() == file
11222            && unit.package_name() == package_name
11223            && if owner_short_name.is_empty() {
11224                // Anonymous enum/union parent: its enumerators carry bare
11225                // short names (#2140), so presence means any ownerless
11226                // field in this file.
11227                !unit.short_name().contains(['.', '$'])
11228            } else {
11229                unit.short_name().starts_with(&prefix)
11230            }
11231    })
11232}
11233
11234/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
11235///
11236/// A region reparse is its own tree and is dropped while the walk that made it
11237/// continues, so a later parse can be allocated at the same address and hand out
11238/// the same node ids.  The root's span and shape pin the identity its address
11239/// alone does not: two roots agreeing on all of this are the same parse of the
11240/// same bytes, and a scan of one is a scan of the other.
11241#[derive(PartialEq, Eq, Hash)]
11242pub struct CppTreeIdentity {
11243    root_id: usize,
11244    start_byte: usize,
11245    end_byte: usize,
11246    kind_id: u16,
11247    child_count: usize,
11248}
11249
11250impl CppTreeIdentity {
11251    fn of(root: Node<'_>) -> Self {
11252        Self {
11253            root_id: root.id(),
11254            start_byte: root.start_byte(),
11255            end_byte: root.end_byte(),
11256            kind_id: root.kind_id(),
11257            child_count: root.child_count(),
11258        }
11259    }
11260}
11261
11262/// The namespace forward declarations one tree's prefix holds, folded in as the
11263/// walk asks about them.
11264///
11265/// `scope_for_recovered_exported_class` asks the same question once per
11266/// recovered class, and the answer depends only on the part of the tree that
11267/// starts before the asking node.  Rescanning that prefix per question is
11268/// quadratic in the file, and a generated header whose parse recovery leaves
11269/// thousands of class-like declarations at file scope pays all of it: 1,904
11270/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
11271/// (#2754).  This carries the scan forward instead.  Each question advances the
11272/// traversal from wherever the last one stopped to the asking node's start byte,
11273/// so a whole walk pays at most one pass over the prefix its furthest question
11274/// reaches, and each question then costs a name lookup.
11275#[derive(Default)]
11276pub struct CppNamespaceForwardScan {
11277    /// Every named node starting before this byte has been folded in.
11278    scanned_through: usize,
11279    forwards: HashMap<String, Vec<CppNamespaceForward>>,
11280}
11281
11282impl CppNamespaceForwardScan {
11283    /// Fold in every named node of `root` that starts at or after the fold
11284    /// watermark and before `cutoff`.
11285    ///
11286    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
11287    /// pass owes are exactly the ones no earlier pass reached, and a question
11288    /// about an earlier byte than one already answered costs nothing.
11289    fn advance_to<'tree>(
11290        &mut self,
11291        root: Node<'tree>,
11292        cutoff: usize,
11293        source: &str,
11294        ancestry: &ParentIndex<'tree>,
11295    ) {
11296        if cutoff <= self.scanned_through {
11297            return;
11298        }
11299        let folded_through = self.scanned_through;
11300        let mut cursor = root.walk();
11301        let mut stack = vec![root];
11302        while let Some(current) = stack.pop() {
11303            if (folded_through..cutoff).contains(&current.start_byte())
11304                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11305            {
11306                self.forwards
11307                    .entry(forward.name.clone())
11308                    .or_default()
11309                    .push(forward);
11310            }
11311            // A subtree ending before the watermark holds only nodes an earlier
11312            // pass already folded, and one starting at or after the cutoff is
11313            // outside the prefix being asked about. Skipping both is what keeps
11314            // the total traversal to one pass.
11315            for child in current.named_children(&mut cursor) {
11316                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
11317                    stack.push(child);
11318                }
11319            }
11320        }
11321        self.scanned_through = cutoff;
11322    }
11323
11324    /// The one namespace `name` was forward declared in before `recovered_node`.
11325    /// More than one matching forward declaration is ambiguous and answers
11326    /// nothing rather than guessing.
11327    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
11328        let mut matching = self
11329            .forwards
11330            .get(name)
11331            .into_iter()
11332            .flatten()
11333            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
11334        let first = matching.next()?;
11335        matching
11336            .next()
11337            .is_none()
11338            .then(|| first.package_name.clone())
11339    }
11340}
11341
11342/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
11343/// debug build checks every answer against (and the one the parity tests drive
11344/// directly).  It walks the whole prefix per question, which is exactly the cost
11345/// #2754 removed from the release path.
11346#[cfg(any(debug_assertions, test))]
11347fn unique_earlier_cpp_namespace_forward<'tree>(
11348    recovered_node: Node<'tree>,
11349    name: &str,
11350    source: &str,
11351    ancestry: &ParentIndex<'tree>,
11352) -> Option<String> {
11353    let mut root = recovered_node;
11354    while let Some(parent) = ancestry.parent(root) {
11355        root = parent;
11356    }
11357
11358    let mut candidates = Vec::new();
11359    let mut stack = vec![root];
11360    while let Some(current) = stack.pop() {
11361        if current.start_byte() < recovered_node.start_byte()
11362            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
11363            && forward.name == name
11364            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
11365        {
11366            candidates.push(forward.package_name);
11367        }
11368
11369        let mut cursor = current.walk();
11370        for child in current.named_children(&mut cursor) {
11371            if child.start_byte() < recovered_node.start_byte() {
11372                stack.push(child);
11373            }
11374        }
11375    }
11376
11377    if candidates.len() == 1 {
11378        candidates.pop()
11379    } else {
11380        None
11381    }
11382}
11383
11384fn malformed_namespace_is_nearest_recovery_region(
11385    namespace_end_byte: usize,
11386    recovered_node: Node<'_>,
11387) -> bool {
11388    let mut root = recovered_node;
11389    while let Some(parent) = root.parent() {
11390        root = parent;
11391    }
11392    let mut cursor = root.walk();
11393    root.named_children(&mut cursor)
11394        .filter(|sibling| {
11395            namespace_end_byte <= sibling.start_byte()
11396                && sibling.end_byte() <= recovered_node.start_byte()
11397        })
11398        .all(is_malformed_namespace_recovery_trivia)
11399}
11400
11401fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
11402    matches!(node.kind(), "ERROR" | "comment")
11403        || node.kind().starts_with("preproc_")
11404        || node.kind() == "expression_statement" && node.named_child_count() == 0
11405}
11406
11407/// Return the namespace path for a forward class only when the declaration is
11408/// at namespace scope.  A declaration nested in a function/class body may share
11409/// the same namespace ancestor but cannot identify a top-level class definition.
11410fn cpp_namespace_name_for_forward<'tree>(
11411    node: Node<'tree>,
11412    source: &str,
11413    ancestry: &ParentIndex<'tree>,
11414) -> Option<String> {
11415    cpp_namespace_definition_for_forward(node, ancestry)?;
11416    cpp_lexical_namespace_name(node, source)
11417}
11418
11419fn cpp_namespace_definition_for_forward<'tree>(
11420    node: Node<'tree>,
11421    ancestry: &ParentIndex<'tree>,
11422) -> Option<Node<'tree>> {
11423    let declaration = ancestry.parent(node)?;
11424    let mut ancestor = ancestry.parent(declaration);
11425    while let Some(current) = ancestor {
11426        if matches!(
11427            current.kind(),
11428            "compound_statement"
11429                | "field_declaration_list"
11430                | "class_specifier"
11431                | "struct_specifier"
11432                | "union_specifier"
11433                | "function_definition"
11434                | "lambda_expression"
11435        ) {
11436            return None;
11437        }
11438        if current.kind() == "namespace_definition" {
11439            return Some(current);
11440        }
11441        ancestor = ancestry.parent(current);
11442    }
11443    None
11444}
11445
11446fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
11447    match node.kind() {
11448        "template_function" => node
11449            .child_by_field_name("name")
11450            .is_some_and(is_function_pointer_like_inner_declarator),
11451        _ => is_pointer_wrapper_declarator(node),
11452    }
11453}
11454
11455fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
11456    match node.kind() {
11457        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
11458        "parenthesized_declarator" => node
11459            .child_by_field_name("declarator")
11460            .or_else(|| last_named_child(node))
11461            .is_some_and(is_pointer_wrapper_declarator),
11462        _ => false,
11463    }
11464}
11465
11466fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
11467    let cleaned = raw_name.trim_start_matches("template ").trim();
11468    // A leading `::` is the explicit-global marker, not an empty owner segment.
11469    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
11470    // erroneous macro envelope swallowing the first identifier of an
11471    // out-of-line `X::X` constructor, chromium #1573); without this strip the
11472    // split below yields owner_parts `[""]`, constructing a unit with an empty
11473    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
11474    let cleaned = cleaned.trim_start_matches("::");
11475    // Parser recovery can preserve two adjacent scope operators around a
11476    // missing component (for example `X::/**/::method` in compiler diagnostic
11477    // fixtures). Empty components are syntax-recovery artifacts, never C++
11478    // owners. Keeping one as the final owner constructed `short_name=".method"`
11479    // and violated the structured package/short boundary during a large LLVM
11480    // workspace build. This is the same legacy-string-to-FqName bridge as the
11481    // ordinary split above; discard only components that the delimiter itself
11482    // proves empty.
11483    let parts: Vec<_> = cleaned
11484        .split("::")
11485        .filter(|component| !component.is_empty())
11486        .collect();
11487    if parts.is_empty() {
11488        return (None, cleaned.to_string(), scope.package_name.clone());
11489    }
11490    if parts.len() > 1 {
11491        let name = parts.last().unwrap_or(&cleaned).to_string();
11492        let owner_parts = &parts[..parts.len() - 1];
11493        if let Some(class_unit) = &scope.class_unit {
11494            // Lexically inside a class body: the owner is that class, whatever
11495            // the declarator re-qualifies it as.
11496            return (
11497                Some(CppMemberOwner::Unit(class_unit.clone())),
11498                name,
11499                scope.package_name.clone(),
11500            );
11501        }
11502        if !scope.package_name.is_empty() {
11503            // Out-of-line member definition written *inside* an enclosing
11504            // `namespace {}` block (scope package is that namespace). Every
11505            // owner segment before the terminal member is a class-nesting step
11506            // -- an out-of-line nested-class member `Outer::Inner::method` in
11507            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
11508            // namespace path: `using namespace` never brings nested-class
11509            // access into unqualified scope, so C++ always writes the full
11510            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
11511            // that redundantly re-states the enclosing namespace it already
11512            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
11513            // strip that re-qualifying prefix (which duplicates a suffix of the
11514            // enclosing package path) before treating what remains as the
11515            // nested-class chain, so the redundant spelling still lands on the
11516            // same `log4cxx.Foo.method` identity as its header declaration.
11517            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
11518            let owner = (!nested.is_empty()).then(|| {
11519                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
11520            });
11521            return (owner, name, scope.package_name.clone());
11522        }
11523        // File scope (no enclosing `namespace {}` block, scope package empty).
11524        let (owner, package_name) = if owner_parts.len() > 1 {
11525            // A multi-segment qualifier at file scope with no enclosing
11526            // namespace: treat all but the last owner segment as the namespace
11527            // path and the last as the owning class (`ns1::ns2::Class::method`
11528            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
11529            // is really a namespace or an outer class cannot be told from the
11530            // declarator text alone here, and no enclosing namespace or
11531            // in-index owner is available at per-file extraction to confirm the
11532            // class reading, so the far-more-common namespace interpretation is
11533            // kept rather than guessed away (the nested-class-at-file-scope and
11534            // using-directive-qualified nested-class shapes remain on this
11535            // behavior; see #1121).
11536            (
11537                Some(CppMemberOwner::Chain(vec![
11538                    owner_parts.last().unwrap_or(&"").to_string(),
11539                ])),
11540                owner_parts[..owner_parts.len() - 1].join("::"),
11541            )
11542        } else {
11543            // A bare `Class::member` qualifier at file scope carries no
11544            // namespace segment of its own. The declarator alone cannot say
11545            // which namespace owns `Class` -- but a `using namespace X;`
11546            // directive already in effect at this point in the file (#1093,
11547            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
11548            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
11549            // is the remaining structural signal for it, so fall back to it
11550            // rather than leaving the definition's package empty while its
11551            // header declaration (parsed inside the `namespace {}` block) keeps
11552            // the real one -- an identity split that made the same member
11553            // unresolvable under its own displayed spelling.
11554            (
11555                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
11556                cpp_using_directive_namespace_for_bare_owner(scope),
11557            )
11558        };
11559        return (owner, name, package_name);
11560    }
11561
11562    let package_name = scope.package_name.clone();
11563    let owner = scope
11564        .class_unit
11565        .as_ref()
11566        .map(|parent| CppMemberOwner::Unit(parent.clone()));
11567    (owner, cleaned.to_string(), package_name)
11568}
11569
11570/// Drop the leading owner segments of an out-of-line member qualifier that
11571/// merely re-state the enclosing namespace the definition already sits in, so
11572/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
11573/// definition may redundantly write `a::b::Outer::Inner::method` (or the
11574/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
11575/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
11576/// noise, not class-nesting steps. Returns the owner segments with the longest
11577/// such re-qualifying prefix removed (possibly all of them, when the qualifier
11578/// names only the enclosing namespace before the terminal member -- a
11579/// re-qualified free function). `package_name` is the enclosing namespace path
11580/// in its stored `::`-joined form; both sides are split on the same delimiter
11581/// the namespace walker joined them with, so this compares namespace *segments*
11582/// rather than scanning text.
11583fn strip_redundant_namespace_prefix<'a>(
11584    owner_parts: &'a [&'a str],
11585    package_name: &str,
11586) -> &'a [&'a str] {
11587    if package_name.is_empty() {
11588        return owner_parts;
11589    }
11590    let package_segments: Vec<&str> = package_name.split("::").collect();
11591    let max_prefix = owner_parts.len().min(package_segments.len());
11592    for prefix_len in (1..=max_prefix).rev() {
11593        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
11594        if &owner_parts[..prefix_len] == package_suffix {
11595            return &owner_parts[prefix_len..];
11596        }
11597    }
11598    owner_parts
11599}
11600
11601/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
11602/// class name at file/namespace scope, from the `using namespace` directives
11603/// visible at this point in the file. Several may be in scope at once (a
11604/// primary `using namespace NS;` alongside deeper conveniences like `using
11605/// namespace NS::helpers;`); since the declarator gives no way to tell which
11606/// one actually declares the owner class, prefer the shallowest (fewest
11607/// `::`-separated segments) as the file's most likely "home" namespace,
11608/// breaking ties by declaration order. Returns an empty string (leaving the
11609/// caller's package unqualified, as before) when no using-namespace directive
11610/// is in scope.
11611fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
11612    scope
11613        .visible_using_namespaces
11614        .iter()
11615        .min_by_key(|namespace| namespace.split("::").count())
11616        .cloned()
11617        .unwrap_or_default()
11618}
11619
11620struct CppQualifiedNameComponent {
11621    name: String,
11622    is_template_id: bool,
11623}
11624
11625/// Canonical nested-class chain for an out-of-line class definition written
11626/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
11627/// component per class (`["Outer", "Inner"]`).
11628///
11629/// The enclosing namespace fixes the namespace/class boundary: after an
11630/// optional redundant spelling of that namespace, every component belongs to
11631/// the class chain. File-scope qualified class names remain untouched because
11632/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
11633///
11634/// The components stay structured (rather than being `$`-joined here) so the
11635/// fq construction can push one Type/Nested segment per class; the `$`-joined
11636/// short-name display form is derived at the call sites that need it.
11637fn qualified_class_name_chain(
11638    class_node: Node<'_>,
11639    source: &str,
11640    scope: &ScopeInfo,
11641) -> Option<Vec<String>> {
11642    if scope.package_name.is_empty() || scope.class_unit.is_some() {
11643        return None;
11644    }
11645    let name = class_node.child_by_field_name("name")?;
11646    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
11647    if explicitly_global
11648        || components.len() < 2
11649        || components.iter().any(|component| component.is_template_id)
11650    {
11651        return None;
11652    }
11653    let names = components
11654        .iter()
11655        .map(|component| component.name.as_str())
11656        .collect::<Vec<_>>();
11657    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
11658    if class_chain.is_empty() {
11659        return None;
11660    }
11661    Some(class_chain.iter().map(|name| name.to_string()).collect())
11662}
11663
11664fn structured_cpp_qualified_components(
11665    qualified_name: Node<'_>,
11666    source: &str,
11667) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
11668    if qualified_name.kind() != "qualified_identifier" {
11669        return None;
11670    }
11671
11672    let mut components = Vec::new();
11673    let mut current = qualified_name;
11674    let mut explicitly_global = false;
11675    loop {
11676        if current.kind() == "qualified_identifier" {
11677            if let Some(component) = current.child_by_field_name("scope") {
11678                components.push(canonical_cpp_qualified_component(component, source)?);
11679            } else if components.is_empty() {
11680                explicitly_global = true;
11681            } else {
11682                return None;
11683            }
11684            current = current.child_by_field_name("name")?;
11685        } else {
11686            components.push(canonical_cpp_qualified_component(current, source)?);
11687            break;
11688        }
11689    }
11690    Some((components, explicitly_global))
11691}
11692
11693fn split_structured_templated_cpp_name(
11694    declarator_name: Node<'_>,
11695    source: &str,
11696    scope: &ScopeInfo,
11697) -> Option<(Option<CppMemberOwner>, String, String)> {
11698    let (mut components, explicitly_global) =
11699        structured_cpp_qualified_components(declarator_name, source)?;
11700
11701    let terminal = components.pop()?;
11702    let owner_start = components
11703        .iter()
11704        .position(|component| component.is_template_id)?;
11705    let explicit_package = components[..owner_start]
11706        .iter()
11707        .map(|component| component.name.as_str())
11708        .collect::<Vec<_>>()
11709        .join("::");
11710    let explicit_package_is_empty = explicit_package.is_empty();
11711    let package_name = match (
11712        explicitly_global,
11713        scope.package_name.is_empty(),
11714        explicit_package_is_empty,
11715    ) {
11716        (true, _, _) => explicit_package,
11717        (false, _, true) => scope.package_name.clone(),
11718        (false, true, false) => explicit_package,
11719        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
11720    };
11721    // Same identity-split fallback as `split_cpp_name` (#1093): a template
11722    // specialization's owner class named with no namespace segment of its own
11723    // (`explicit_package` empty) at file scope (`explicitly_global` false)
11724    // with nothing enclosing (`package_name` still empty) has no structural
11725    // signal for its namespace besides an in-scope `using namespace X;`.
11726    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
11727    {
11728        cpp_using_directive_namespace_for_bare_owner(scope)
11729    } else {
11730        package_name
11731    };
11732    let owner_chain = components[owner_start..]
11733        .iter()
11734        .map(|component| component.name.clone())
11735        .collect::<Vec<_>>();
11736    if owner_chain.is_empty() || terminal.name.is_empty() {
11737        return None;
11738    }
11739
11740    Some((
11741        Some(CppMemberOwner::Chain(owner_chain)),
11742        terminal.name,
11743        package_name,
11744    ))
11745}
11746
11747fn canonical_cpp_qualified_component(
11748    mut component: Node<'_>,
11749    source: &str,
11750) -> Option<CppQualifiedNameComponent> {
11751    let mut is_template_id = false;
11752    loop {
11753        match component.kind() {
11754            "template_type" => {
11755                is_template_id = true;
11756                component = component.child_by_field_name("name")?;
11757            }
11758            "dependent_name" => component = component.named_child(0)?,
11759            "identifier"
11760            | "field_identifier"
11761            | "namespace_identifier"
11762            | "type_identifier"
11763            | "operator_name"
11764            | "destructor_name" => {
11765                let name = normalize_cpp_whitespace(node_text(component, source));
11766                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
11767                    name,
11768                    is_template_id,
11769                });
11770            }
11771            _ => component = component.child_by_field_name("name")?,
11772        }
11773    }
11774}
11775
11776fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
11777    if let Some(name) = macro_decorated_unqualified_name(node) {
11778        return extract_declarator_name(name, source);
11779    }
11780    match node.kind() {
11781        "identifier"
11782        | "field_identifier"
11783        | "type_identifier"
11784        | "operator_name"
11785        | "destructor_name"
11786        | "qualified_identifier" => node_text(node, source).to_string(),
11787        "function_declarator"
11788        | "pointer_declarator"
11789        | "reference_declarator"
11790        | "parenthesized_declarator"
11791        | "array_declarator"
11792        | "template_function" => node
11793            .child_by_field_name("declarator")
11794            .or_else(|| node.child_by_field_name("name"))
11795            .or_else(|| last_named_child(node))
11796            .map(|child| extract_declarator_name(child, source))
11797            .unwrap_or_else(|| node_text(node, source).to_string()),
11798        _ => node
11799            .child_by_field_name("name")
11800            .map(|child| extract_declarator_name(child, source))
11801            .unwrap_or_else(|| node_text(node, source).to_string()),
11802    }
11803}
11804
11805/// Extract a callable identity only through declaration-shaped AST nodes.
11806/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
11807/// expose the call's parameter list as a false function declarator; accepting
11808/// arbitrary node text there emitted bogus names such as `.*f`.
11809fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
11810    if let Some(name) = macro_decorated_unqualified_name(node) {
11811        return extract_callable_declarator_name(name, source);
11812    }
11813    match node.kind() {
11814        "identifier"
11815        | "field_identifier"
11816        | "type_identifier"
11817        | "operator_name"
11818        | "destructor_name"
11819        | "qualified_identifier" => Some(node_text(node, source).to_string()),
11820        "function_declarator"
11821        | "pointer_declarator"
11822        | "reference_declarator"
11823        | "parenthesized_declarator"
11824        | "array_declarator"
11825        | "template_function" => node
11826            .child_by_field_name("declarator")
11827            .or_else(|| node.child_by_field_name("name"))
11828            .and_then(|child| extract_callable_declarator_name(child, source)),
11829        _ => None,
11830    }
11831}
11832
11833fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
11834    loop {
11835        if let Some(name) = macro_decorated_unqualified_name(node) {
11836            node = name;
11837            continue;
11838        }
11839        match node.kind() {
11840            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
11841                let name = node_text(node, source).trim().to_string();
11842                return (!name.is_empty()).then_some(name);
11843            }
11844            _ => {
11845                node = node
11846                    .child_by_field_name("declarator")
11847                    .or_else(|| node.child_by_field_name("name"))
11848                    .or_else(|| last_named_child(node))?;
11849            }
11850        }
11851    }
11852}
11853
11854/// Recover a C field whose name is wrapped in the function-like
11855/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
11856/// function declarator, while retaining its argument as a structured child.
11857/// The exact macro name and one-argument shape keep ordinary function-pointer
11858/// fields and unknown malformed declarators fail-closed.
11859#[derive(Clone, Copy)]
11860pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
11861    pub(crate) name: Node<'tree>,
11862    pub(crate) declarator: Node<'tree>,
11863}
11864
11865impl RecoveredFunctionLikeFieldDeclarator<'_> {
11866    pub(crate) fn pointer_depth(self) -> i32 {
11867        let mut depth = 0;
11868        let mut current = self.declarator;
11869        while current.kind() != "function_declarator" {
11870            if current.kind() == "pointer_declarator" {
11871                depth += 1;
11872            }
11873            current = current
11874                .child_by_field_name("declarator")
11875                .expect("recovered field wrapper has an inner declarator");
11876        }
11877        depth
11878    }
11879}
11880
11881/// Whether a class-scope field declaration is really a function-like macro
11882/// invocation whose replacement the parser never saw.
11883///
11884/// The invocation carries no terminator of its own, so tree-sitter invents a
11885/// zero-width `;` and reads the macro name as the member's type and the
11886/// argument list as a `parenthesized_declarator`. That shape is not a member:
11887/// a member terminates itself, and with a real terminator the grammar reads
11888/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
11889/// type can name the macro being invoked; a builtin type keeps whatever
11890/// recovery the ordinary declarator walk makes of it.
11891fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
11892    if node.kind() != "field_declaration" {
11893        return false;
11894    }
11895    let Some(type_node) = node.child_by_field_name("type") else {
11896        return false;
11897    };
11898    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
11899        return false;
11900    }
11901    let Some(declarator) = node.child_by_field_name("declarator") else {
11902        return false;
11903    };
11904    if declarator.kind() != "parenthesized_declarator" {
11905        return false;
11906    }
11907    let mut invented_terminator = false;
11908    for child in children_iter(node) {
11909        if child.kind() != ";" {
11910            continue;
11911        }
11912        if !child.is_missing() {
11913            return false;
11914        }
11915        invented_terminator = true;
11916    }
11917    invented_terminator
11918}
11919
11920pub(crate) fn recovered_function_like_field_declarator<'tree>(
11921    node: Node<'tree>,
11922    source: &str,
11923) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
11924    if node.kind() != "field_declaration" {
11925        return None;
11926    }
11927    let outer_declarator = node.child_by_field_name("declarator")?;
11928    let mut declarator = outer_declarator;
11929    while matches!(
11930        declarator.kind(),
11931        "pointer_declarator"
11932            | "reference_declarator"
11933            | "array_declarator"
11934            | "parenthesized_declarator"
11935    ) {
11936        declarator = declarator.child_by_field_name("declarator")?;
11937    }
11938    if declarator.kind() != "function_declarator" {
11939        return None;
11940    }
11941    let macro_name = declarator.child_by_field_name("declarator")?;
11942    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
11943    {
11944        return None;
11945    }
11946    let parameters = declarator.child_by_field_name("parameters")?;
11947    let mut cursor = parameters.walk();
11948    let mut arguments = parameters.named_children(&mut cursor);
11949    let parameter = arguments.next()?;
11950    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
11951        return None;
11952    }
11953    let name = parameter.child_by_field_name("type").filter(|argument| {
11954        matches!(
11955            argument.kind(),
11956            "identifier" | "field_identifier" | "type_identifier"
11957        )
11958    })?;
11959    Some(RecoveredFunctionLikeFieldDeclarator {
11960        name,
11961        declarator: outer_declarator,
11962    })
11963}
11964
11965use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
11966
11967fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
11968    let name_node = node.child_by_field_name("name")?;
11969    let name = normalize_cpp_whitespace(node_text(name_node, source));
11970    (!name.is_empty()).then_some(name)
11971}
11972
11973fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
11974    if node.kind() != "declaration" {
11975        return Vec::new();
11976    }
11977    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
11978        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
11979    }) else {
11980        return Vec::new();
11981    };
11982    let Some(declarator) = node.child_by_field_name("declarator") else {
11983        return Vec::new();
11984    };
11985    if node_text(keyword, source) == "using"
11986        && (declarator.kind() != "init_declarator"
11987            || declarator.child_by_field_name("value").is_none())
11988    {
11989        return Vec::new();
11990    }
11991    if node_text(keyword, source) == "typedef"
11992        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
11993    {
11994        return vec![alias_name];
11995    }
11996    extract_typedef_declarator_name(declarator, source)
11997        .into_iter()
11998        .collect()
11999}
12000
12001/// The RHS retained by the same malformed alias shapes declaration admission
12002/// accepts. The alias name proves which declarator owns this source evidence.
12003pub(crate) fn recovered_alias_type_node<'tree>(
12004    node: Node<'tree>,
12005    alias_name: &str,
12006    source: &str,
12007) -> Option<Node<'tree>> {
12008    if node.kind() != "declaration" {
12009        return None;
12010    }
12011    let declarator = node.child_by_field_name("declarator")?;
12012    let admitted = recovered_type_alias_names(node, source)
12013        .iter()
12014        .any(|name| name == alias_name)
12015        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
12016    if !admitted {
12017        return None;
12018    }
12019    if declarator.kind() == "init_declarator" {
12020        return declarator.child_by_field_name("value");
12021    }
12022    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
12023        .then_some(declarator)
12024}
12025
12026fn recovered_typedef_error_alias_name(
12027    declaration: Node<'_>,
12028    declarator: Node<'_>,
12029    source: &str,
12030) -> Option<String> {
12031    // An export macro between `class` and its name can make tree-sitter parse
12032    // the recovered class body as a function body. In that shape,
12033    //
12034    //     typedef spi::Filter BASE_CLASS;
12035    //
12036    // becomes a declaration whose `declarator` is the underlying qualified
12037    // type (`spi::Filter`) and whose actual alias name is displaced into the
12038    // following ERROR node. Do not publish the terminal underlying type
12039    // (`Filter`) as a false class-owned alias.
12040    if declarator.kind() != "qualified_identifier" {
12041        return None;
12042    }
12043    let mut cursor = declaration.walk();
12044    let mut errors = declaration
12045        .named_children(&mut cursor)
12046        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
12047    let error = errors.next()?;
12048    if errors.next().is_some() || error.named_child_count() != 1 {
12049        return None;
12050    }
12051    let name = error.named_child(0)?;
12052    if !matches!(
12053        name.kind(),
12054        "identifier" | "field_identifier" | "type_identifier"
12055    ) {
12056        return None;
12057    }
12058    let name = normalize_cpp_whitespace(node_text(name, source));
12059    (!name.is_empty()).then_some(name)
12060}
12061
12062fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
12063    // A function-like token in the type position can make tree-sitter expose
12064    // its argument as a parenthesized declarator. Do not publish that argument
12065    // as an alias. The macro-specific recovery below handles the proven shape.
12066    if fragmented_parenthesized_typedef_type(node).is_some() {
12067        return Vec::new();
12068    }
12069    let has_function_like_macro_type = node
12070        .child_by_field_name("type")
12071        .filter(|type_node| type_node.kind() == "type_identifier")
12072        .is_some_and(|type_node| {
12073            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
12074        });
12075    let mut names = Vec::new();
12076    let mut cursor = node.walk();
12077    for declarator in node.children_by_field_name("declarator", &mut cursor) {
12078        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
12079            continue;
12080        }
12081        if let Some(name) = extract_typedef_declarator_name(declarator, source)
12082            && !names.contains(&name)
12083        {
12084            names.push(name);
12085        }
12086    }
12087    names
12088}
12089
12090struct RecoveredMacroTypedefAlias<'tree> {
12091    name: String,
12092    end_node: Node<'tree>,
12093}
12094
12095/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
12096/// into an identifier expression statement. The uppercase macro token, missing
12097/// typedef terminator, and complete sibling terminator prove this exact shape.
12098fn recovered_macro_typedef_alias<'tree>(
12099    node: Node<'tree>,
12100    source: &str,
12101) -> Option<RecoveredMacroTypedefAlias<'tree>> {
12102    let type_node = fragmented_parenthesized_typedef_type(node)?;
12103    if type_node.kind() != "type_identifier"
12104        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
12105    {
12106        return None;
12107    }
12108
12109    let end_node = node.next_named_sibling()?;
12110    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
12111        return None;
12112    }
12113    let name_node = end_node.named_child(0)?;
12114    if name_node.kind() != "identifier" {
12115        return None;
12116    }
12117    let has_terminator = (0..end_node.child_count()).any(|index| {
12118        end_node
12119            .child(index)
12120            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
12121    });
12122    if !has_terminator {
12123        return None;
12124    }
12125    let name = normalize_cpp_whitespace(node_text(name_node, source));
12126    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
12127}
12128
12129fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
12130    if node.kind() != "type_definition" {
12131        return None;
12132    }
12133    let mut declarator_cursor = node.walk();
12134    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
12135    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
12136        return None;
12137    }
12138    let has_missing_terminator = (0..node.child_count()).any(|index| {
12139        node.child(index)
12140            .is_some_and(|child| child.kind() == ";" && child.is_missing())
12141    });
12142    if !has_missing_terminator {
12143        return None;
12144    }
12145    node.child_by_field_name("type")
12146}
12147
12148pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12149    match node.kind() {
12150        "identifier" | "field_identifier" | "type_identifier" => {
12151            direct_identifier_name(node, source)
12152        }
12153        "qualified_identifier" => node
12154            .child_by_field_name("name")
12155            .and_then(|name| extract_typedef_declarator_name(name, source)),
12156        _ => node
12157            .child_by_field_name("declarator")
12158            .or_else(|| node.child_by_field_name("name"))
12159            .or_else(|| last_named_child(node))
12160            .and_then(|child| extract_typedef_declarator_name(child, source)),
12161    }
12162}
12163
12164fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
12165    if let Some(name_node) = node.child_by_field_name("name") {
12166        let name = normalize_cpp_whitespace(node_text(name_node, source));
12167        return (!name.is_empty()).then_some(name);
12168    }
12169    let mut cursor = node.walk();
12170    node.named_children(&mut cursor)
12171        .find(|child| {
12172            matches!(
12173                child.kind(),
12174                "identifier" | "field_identifier" | "type_identifier"
12175            )
12176        })
12177        .and_then(|name_node| direct_identifier_name(name_node, source))
12178}
12179
12180fn render_cpp_type_signature(
12181    node: Node<'_>,
12182    source: &str,
12183    template_signature: Option<&str>,
12184) -> String {
12185    let text = normalize_cpp_whitespace(node_text(node, source));
12186    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
12187    let rendered = if head.ends_with(';') {
12188        head.to_string()
12189    } else {
12190        format!("{head} {{")
12191    };
12192    if let Some(template_signature) = template_signature {
12193        format!("template {template_signature} {rendered}")
12194    } else {
12195        rendered
12196    }
12197}
12198
12199fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
12200    if let Some(recovered) = recovered_pyobject_head_field(node, source)
12201        && recovered.declarator == declarator
12202    {
12203        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
12204        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
12205        return format!("{type_text} {declarator};");
12206    }
12207    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
12208        && recovered.name == declarator
12209    {
12210        let type_text = node
12211            .child_by_field_name("type")
12212            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
12213            .unwrap_or_default();
12214        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
12215        let mut prefix = String::new();
12216        let mut suffix = String::new();
12217        let mut current = recovered.declarator;
12218        while current.kind() != "function_declarator" {
12219            match current.kind() {
12220                "pointer_declarator" => prefix.push('*'),
12221                "reference_declarator" => prefix.push('&'),
12222                "array_declarator" => {
12223                    let size = current
12224                        .child_by_field_name("size")
12225                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
12226                        .unwrap_or_default();
12227                    suffix.push('[');
12228                    suffix.push_str(&size);
12229                    suffix.push(']');
12230                }
12231                "parenthesized_declarator" => {}
12232                _ => unreachable!("validated recovered field declarator wrapper"),
12233            }
12234            current = current
12235                .child_by_field_name("declarator")
12236                .expect("recovered field wrapper has an inner declarator");
12237        }
12238        let separator = if prefix.is_empty() { "" } else { " " };
12239        return format!("{type_text} {prefix}{separator}{name}{suffix};");
12240    }
12241    if let Some(signature) =
12242        render_recovered_macro_qualified_field_signature(node, declarator, source)
12243    {
12244        return signature;
12245    }
12246    let prefix = cpp_declaration_prefix(node, source);
12247    let name = extract_variable_name(declarator, source).unwrap_or_default();
12248    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
12249    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
12250        || (prefix.ends_with('&') && raw_suffix == "&")
12251    {
12252        String::new()
12253    } else {
12254        raw_suffix
12255    };
12256
12257    let mut rendered = if suffix.is_empty() {
12258        format!("{prefix} {name}")
12259    } else if suffix.starts_with('*') || suffix.starts_with('&') {
12260        format!("{prefix}{suffix} {name}")
12261    } else if suffix.starts_with('[') || suffix.starts_with('(') {
12262        format!("{prefix} {name}{suffix}")
12263    } else {
12264        format!("{prefix} {suffix}{name}")
12265    };
12266    rendered = collapse_cpp_whitespace(&rendered);
12267
12268    // Check the terminal token directly instead of normalizing the whole
12269    // declaration, which can copy a large initializer for each field.
12270    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
12271        format!("{rendered} = {initializer};")
12272    } else if cpp_direct_semicolon(node).is_some() {
12273        format!("{rendered};")
12274    } else {
12275        rendered
12276    }
12277}
12278
12279fn render_recovered_macro_qualified_field_signature(
12280    node: Node<'_>,
12281    declarator: Node<'_>,
12282    source: &str,
12283) -> Option<String> {
12284    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
12285    if !recovered
12286        .iter()
12287        .any(|candidate| same_node(*candidate, declarator))
12288    {
12289        return None;
12290    }
12291    let pseudo_declarator = node.child_by_field_name("declarator")?;
12292    let mut cursor = node.walk();
12293    let clause = node
12294        .named_children(&mut cursor)
12295        .find(|child| child.kind() == "bitfield_clause")?;
12296    let mut cursor = clause.walk();
12297    let error = clause
12298        .named_children(&mut cursor)
12299        .find(|child| child.kind() == "ERROR")?;
12300    let qualified_type =
12301        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
12302    let prefix = cpp_declaration_prefix(node, source);
12303    let name = extract_variable_name(declarator, source)?;
12304    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
12305    let mut rendered = if suffix.is_empty() {
12306        format!("{prefix} {qualified_type} {name}")
12307    } else {
12308        format!("{prefix} {qualified_type} {suffix} {name}")
12309    };
12310    rendered = collapse_cpp_whitespace(&rendered);
12311
12312    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
12313        Some(format!(
12314            "{rendered} = {};",
12315            normalize_cpp_whitespace(node_text(initializer, source))
12316        ))
12317    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
12318        Some(format!("{rendered} = {initializer};"))
12319    } else {
12320        Some(format!("{rendered};"))
12321    }
12322}
12323
12324fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
12325    match node.kind() {
12326        "pointer_expression" => {
12327            let operator = node
12328                .child_by_field_name("operator")
12329                .or_else(|| node.child(0))
12330                .map(|operator| node_text(operator, source))
12331                .unwrap_or("*");
12332            let argument = node
12333                .child_by_field_name("argument")
12334                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12335                .unwrap_or_default();
12336            format!("{operator}{argument}")
12337        }
12338        "unary_expression" => {
12339            let operator = node
12340                .child_by_field_name("operator")
12341                .or_else(|| node.child(0))
12342                .map(|operator| node_text(operator, source))
12343                .unwrap_or_default();
12344            let argument = node
12345                .child_by_field_name("argument")
12346                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
12347                .unwrap_or_default();
12348            format!("{operator}{argument}")
12349        }
12350        "identifier" | "field_identifier" => String::new(),
12351        _ => cpp_declarator_suffix_without_name(node, source),
12352    }
12353}
12354
12355fn recovered_macro_qualified_field_initializer<'tree>(
12356    clause: Node<'tree>,
12357    declarator: Node<'tree>,
12358) -> Option<Node<'tree>> {
12359    let mut stack = vec![clause];
12360    while let Some(current) = stack.pop() {
12361        if current.kind() == "assignment_expression"
12362            && current
12363                .child_by_field_name("left")
12364                .is_some_and(|left| same_node(left, declarator))
12365        {
12366            return current.child_by_field_name("right");
12367        }
12368        let mut cursor = current.walk();
12369        stack.extend(current.named_children(&mut cursor));
12370    }
12371    None
12372}
12373
12374fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
12375    let text = node_text(node, source);
12376    let mut cursor = node.walk();
12377    let first_declarator = node.named_children(&mut cursor).find(|child| {
12378        matches!(
12379            child.kind(),
12380            "init_declarator"
12381                | "identifier"
12382                | "field_identifier"
12383                | "pointer_declarator"
12384                | "reference_declarator"
12385                | "array_declarator"
12386                | "function_declarator"
12387        )
12388    });
12389    let prefix = if let Some(first_declarator) = first_declarator {
12390        let end = first_declarator
12391            .start_byte()
12392            .saturating_sub(node.start_byte());
12393        let mut prefix = text.get(..end).unwrap_or(text).to_string();
12394        let declarator_suffix = match first_declarator.kind() {
12395            "init_declarator" => first_declarator
12396                .child_by_field_name("declarator")
12397                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
12398                .unwrap_or_default(),
12399            _ => cpp_declarator_suffix_without_name(first_declarator, source),
12400        };
12401        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
12402            prefix.push_str(&declarator_suffix);
12403        }
12404        return collapse_cpp_whitespace(&prefix)
12405            .trim_end_matches(',')
12406            .trim_end_matches(';')
12407            .trim()
12408            .to_string();
12409    } else {
12410        text
12411    };
12412    collapse_cpp_whitespace(prefix)
12413        .trim_end_matches(',')
12414        .trim_end_matches(';')
12415        .trim()
12416        .to_string()
12417}
12418
12419fn cpp_preserved_initializer(
12420    declaration_node: Node<'_>,
12421    declarator: Node<'_>,
12422    source: &str,
12423) -> Option<String> {
12424    let name = extract_variable_name(declarator, source)?;
12425    let mut cursor = declaration_node.walk();
12426    for child in declaration_node.named_children(&mut cursor) {
12427        if child.kind() != "init_declarator" {
12428            continue;
12429        }
12430        let Some(inner) = child.child_by_field_name("declarator") else {
12431            continue;
12432        };
12433        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
12434            continue;
12435        }
12436        let value = child.child_by_field_name("value")?;
12437        let kind = value.kind();
12438        if matches!(
12439            kind,
12440            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
12441        ) {
12442            return Some(normalize_cpp_whitespace(node_text(value, source)));
12443        }
12444        break;
12445    }
12446    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
12447    let pattern = format!(
12448        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
12449        regex::escape(&name)
12450    );
12451    Regex::new(&pattern)
12452        .ok()
12453        .and_then(|regex| regex.captures(&declaration_text))
12454        .and_then(|captures| captures.get(1))
12455        .map(|value| value.as_str().to_string())
12456}
12457
12458fn render_cpp_function_display_signature_from_node<'tree>(
12459    node: Node<'tree>,
12460    source: &str,
12461    template_signature: Option<&str>,
12462    has_body: bool,
12463    ancestry: &ParentIndex<'tree>,
12464) -> String {
12465    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
12466    let parent_text = node_text(root, source);
12467    let body_local_start = root
12468        .child_by_field_name("body")
12469        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
12470        .unwrap_or(parent_text.len());
12471    let display = parent_text
12472        .get(..body_local_start)
12473        .unwrap_or(parent_text)
12474        .trim()
12475        .trim();
12476    let display = if let Some(template_signature) = template_signature {
12477        if display.starts_with("template ") {
12478            display.to_string()
12479        } else {
12480            format!("template {template_signature} {display}")
12481        }
12482    } else {
12483        display.to_string()
12484    };
12485    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
12486    if has_body {
12487        format!("{display} {{...}}")
12488    } else {
12489        format!("{display};")
12490    }
12491}
12492
12493fn cpp_template_signature(
12494    template_node: Node<'_>,
12495    declaration_child: Node<'_>,
12496    source: &str,
12497) -> Option<String> {
12498    let text = source
12499        .get(template_node.start_byte()..declaration_child.start_byte())
12500        .unwrap_or("");
12501    let text = normalize_cpp_whitespace(text);
12502    let start = text.find('<')?;
12503    let end = text.rfind('>')?;
12504    if end < start {
12505        return None;
12506    }
12507    Some(text[start..=end].to_string())
12508}
12509
12510struct RecoveredFragmentedPartialSpecialization<'tree> {
12511    declaration_node: Node<'tree>,
12512    name: String,
12513    range: Range,
12514    prefix_members: Vec<Node<'tree>>,
12515    member_siblings: Vec<Node<'tree>>,
12516    following_declarations: Vec<Node<'tree>>,
12517}
12518
12519struct RecoveredFragmentedPreprocessorClass<'tree> {
12520    declaration_node: Node<'tree>,
12521    class_node: Node<'tree>,
12522    body: Node<'tree>,
12523    name: String,
12524    range: Range,
12525    tail_members: Vec<Node<'tree>>,
12526    member_siblings: Vec<Node<'tree>>,
12527}
12528
12529/// Recover a class whose preprocessor-fragmented parse closes at an early
12530/// member body and publishes the remaining in-class declarations as siblings
12531/// of the surrounding alternative. Primary classes are admitted only when an
12532/// earlier branch contains the matching bodyless declaration and the class
12533/// node retains the displaced `#endif`. Partial specializations instead carry
12534/// their identity structurally in the `template_type` name and template
12535/// metadata. Retain the original AST nodes and re-own only the siblings through
12536/// the displaced structural `};` terminator.
12537fn recover_fragmented_preprocessor_class<'tree>(
12538    template_node: Node<'tree>,
12539    source: &str,
12540    ancestry: &ParentIndex<'tree>,
12541) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
12542    let alternative = ancestry.parent(template_node)?;
12543    if alternative.kind() != "preproc_else" {
12544        return None;
12545    }
12546    let conditional = alternative.parent()?;
12547    if conditional.kind() != "preproc_if" {
12548        return None;
12549    }
12550    let declaration_node = template_node
12551        .named_children(&mut template_node.walk())
12552        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
12553    let class_node = declaration_node
12554        .named_children(&mut declaration_node.walk())
12555        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
12556    let body = cpp_body_node(class_node)?;
12557    if class_node.end_byte() >= declaration_node.end_byte() {
12558        return None;
12559    }
12560    let name = class_like_name(class_node, source, ancestry)?;
12561    let is_partial_specialization = class_node
12562        .child_by_field_name("name")
12563        .is_some_and(|class_name| class_name.kind() == "template_type");
12564    if is_partial_specialization {
12565        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
12566        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
12567            return None;
12568        }
12569    } else {
12570        if !class_has_displaced_preprocessor_terminator(class_node) {
12571            return None;
12572        }
12573        let matching_other_branch = conditional
12574            .named_children(&mut conditional.walk())
12575            .take_while(|child| !same_node(*child, alternative))
12576            .filter(|child| child.kind() == "template_declaration")
12577            .filter_map(first_class_like_child)
12578            .any(|candidate| {
12579                cpp_body_node(candidate).is_none()
12580                    && class_like_name(candidate, source, ancestry).as_deref()
12581                        == Some(name.as_str())
12582            });
12583        if !matching_other_branch {
12584            return None;
12585        }
12586    }
12587
12588    let mut tail_members = Vec::new();
12589    let mut saw_class = false;
12590    let mut declaration_cursor = declaration_node.walk();
12591    for child in declaration_node.named_children(&mut declaration_cursor) {
12592        if same_node(child, class_node) {
12593            saw_class = true;
12594        } else if saw_class {
12595            tail_members.push(child);
12596        }
12597    }
12598
12599    let mut member_siblings = Vec::new();
12600    let mut saw_template = false;
12601    let mut terminator = None;
12602    for index in 0..alternative.child_count() {
12603        let Some(child) = alternative.child(index) else {
12604            continue;
12605        };
12606        if same_node(child, template_node) {
12607            saw_template = true;
12608            continue;
12609        }
12610        if !saw_template {
12611            continue;
12612        }
12613        if displaced_fragmented_class_terminator(alternative, index) {
12614            terminator = alternative.child(index + 1);
12615            break;
12616        }
12617        if child.is_named() {
12618            member_siblings.push(child);
12619        }
12620    }
12621    let terminator = terminator?;
12622    Some(RecoveredFragmentedPreprocessorClass {
12623        declaration_node,
12624        class_node,
12625        body,
12626        name,
12627        range: Range {
12628            start_byte: class_node.start_byte(),
12629            end_byte: terminator.end_byte(),
12630            start_line: class_node.start_position().row + 1,
12631            end_line: terminator.end_position().row + 1,
12632        },
12633        tail_members,
12634        member_siblings,
12635    })
12636}
12637
12638fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
12639    (0..class_node.child_count()).any(|index| {
12640        class_node.child(index).is_some_and(|child| {
12641            child.kind() == "ERROR"
12642                && (0..child.child_count()).any(|error_index| {
12643                    child
12644                        .child(error_index)
12645                        .is_some_and(|token| token.kind() == "#endif")
12646                })
12647        })
12648    })
12649}
12650
12651/// The real `#endif` that tree-sitter consumed inside an error subtree.
12652///
12653/// A preprocessor directive inside a malformed array bound can cause later
12654/// declarations to remain children of the conditional. The non-missing token
12655/// still gives the exact structured boundary. Ignore nested conditionals and
12656/// select the last unpaired error-owned token. Tree-sitter can pair a later
12657/// outer `#endif` with this conditional, so the direct terminator is not
12658/// necessarily missing.
12659pub fn cpp_displaced_preprocessor_terminator<'tree>(
12660    conditional: Node<'tree>,
12661) -> Option<Node<'tree>> {
12662    if !conditional.has_error() {
12663        return None;
12664    }
12665    let has_concrete_direct_terminator = conditional
12666        .child_count()
12667        .checked_sub(1)
12668        .and_then(|index| conditional.child(index))
12669        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
12670    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
12671        // A structured alternative proves that the direct `#endif` closes
12672        // this family. An error-owned terminator inside either branch belongs
12673        // to a damaged nested conditional, not to this one.
12674        return None;
12675    }
12676    // Pair concrete directive tokens while walking the recovered subtree. An
12677    // inner conditional may be represented only by its `#ifndef`/`#endif`
12678    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
12679    // sufficient: an inner `#endif` can otherwise be mistaken for this
12680    // conditional's displaced terminator.
12681    let mut displaced = None;
12682    let mut conditional_depth = 0usize;
12683    let mut stack = children_iter(conditional)
12684        .map(|child| (child, false))
12685        .collect::<Vec<_>>();
12686    stack.reverse();
12687    while let Some((node, inside_error)) = stack.pop() {
12688        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
12689            continue;
12690        }
12691        if node != conditional
12692            && matches!(
12693                node.kind(),
12694                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
12695            )
12696        {
12697            continue;
12698        }
12699        let inside_error = inside_error || node.kind() == "ERROR";
12700        match node.kind() {
12701            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
12702                conditional_depth += 1;
12703            }
12704            "#endif" if !node.is_missing() => {
12705                if conditional_depth == 0
12706                    && inside_error
12707                    && displaced
12708                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
12709                {
12710                    displaced = Some(node);
12711                }
12712                conditional_depth = conditional_depth.saturating_sub(1);
12713            }
12714            _ => {}
12715        }
12716        let first_pushed = stack.len();
12717        stack.extend(children_iter(node).map(|child| (child, inside_error)));
12718        stack[first_pushed..].reverse();
12719    }
12720    displaced
12721}
12722
12723/// The effective end of a conditional whose real terminator tree-sitter
12724/// displaced into declaration recovery.
12725///
12726/// Most damaged conditionals retain a concrete `#endif` token below an
12727/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
12728/// boundary. A preprocessor family that selects the middle of a declaration
12729/// can lose the directive tokens entirely. In that shape tree-sitter leaves
12730/// the declaration's `typedef` token as the sole child of the immediately
12731/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
12732/// declarator name inside the conditional's first declaration. The declaration
12733/// end is then the smallest structured boundary that contains the whole split
12734/// declaration.
12735#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12736pub struct CppDisplacedPreprocessorBoundary {
12737    pub end_byte: usize,
12738    pub end_line: usize,
12739}
12740
12741pub fn cpp_displaced_preprocessor_boundary(
12742    conditional: Node<'_>,
12743) -> Option<CppDisplacedPreprocessorBoundary> {
12744    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
12745        return Some(CppDisplacedPreprocessorBoundary {
12746            end_byte: terminator.end_byte(),
12747            end_line: terminator.end_position().row + 1,
12748        });
12749    }
12750    if let Some(declaration) = displaced_split_declaration(conditional) {
12751        return Some(CppDisplacedPreprocessorBoundary {
12752            end_byte: declaration.end_byte(),
12753            end_line: declaration.end_position().row + 1,
12754        });
12755    }
12756    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
12757        return Some(CppDisplacedPreprocessorBoundary {
12758            end_byte: terminator.end_byte(),
12759            end_line: terminator.end_position().row + 1,
12760        });
12761    }
12762    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
12763        return Some(CppDisplacedPreprocessorBoundary {
12764            end_byte: terminator.end_byte(),
12765            end_line: terminator.end_position().row + 1,
12766        });
12767    }
12768    None
12769}
12770
12771/// Recover an outer terminator that tree-sitter assigned to a damaged nested
12772/// conditional. This occurs when a split construct such as `extern "C"`
12773/// consumes the nested `#endif` inside an error node: the nested conditional's
12774/// direct terminator is then the outer conditional's real terminator, while
12775/// the outer node ends with a missing token and absorbs later declarations.
12776fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12777    if !conditional.has_error()
12778        || conditional.child_by_field_name("alternative").is_some()
12779        || conditional
12780            .child(conditional.child_count().saturating_sub(1))
12781            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
12782    {
12783        return None;
12784    }
12785    let mut recovered = None;
12786    for nested in named_children_iter(conditional) {
12787        if !matches!(
12788            nested.kind(),
12789            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
12790        ) || nested.child_by_field_name("alternative").is_some()
12791        {
12792            continue;
12793        }
12794        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
12795            continue;
12796        };
12797        if direct.kind() != "#endif" || direct.is_missing() {
12798            continue;
12799        }
12800        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
12801            continue;
12802        };
12803        if displaced.end_byte() >= direct.start_byte() {
12804            continue;
12805        }
12806        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
12807            recovered = Some(direct);
12808        }
12809    }
12810    recovered
12811}
12812
12813fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12814    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
12815        return None;
12816    }
12817    let mut cursor = conditional.walk();
12818    let declarations = conditional
12819        .named_children(&mut cursor)
12820        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
12821        .collect::<Vec<_>>();
12822    let declaration = *declarations.first()?;
12823    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
12824        return None;
12825    }
12826    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
12827    let mut terminator = None;
12828    let mut stack = (0..declaration.child_count())
12829        .filter_map(|index| declaration.child(index))
12830        .filter(|child| child.start_byte() < declarator_start)
12831        .map(|child| (child, false))
12832        .collect::<Vec<_>>();
12833    while let Some((node, inside_error)) = stack.pop() {
12834        let inside_error = inside_error || node.kind() == "ERROR";
12835        if inside_error && node.kind() == "#endif" && !node.is_missing() {
12836            terminator = Some(node);
12837            continue;
12838        }
12839        for index in 0..node.child_count() {
12840            if let Some(child) = node.child(index)
12841                && child.start_byte() < declarator_start
12842            {
12843                stack.push((child, inside_error));
12844            }
12845        }
12846    }
12847    terminator
12848}
12849
12850fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
12851    if !conditional.has_error()
12852        || conditional.child_by_field_name("alternative").is_some()
12853        || conditional
12854            .prev_named_sibling()
12855            .filter(|sibling| {
12856                sibling.kind() == "ERROR"
12857                    && sibling.child_count() == 1
12858                    && sibling
12859                        .child(0)
12860                        .is_some_and(|child| child.kind() == "typedef")
12861            })
12862            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
12863            .is_none()
12864    {
12865        return None;
12866    }
12867    let mut cursor = conditional.walk();
12868    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
12869    let declaration_index = children
12870        .iter()
12871        .position(|child| child.kind() == "declaration" && child.has_error())?;
12872    let declaration = children[declaration_index];
12873    if !children
12874        .iter()
12875        .skip(declaration_index + 1)
12876        .any(|child| child.end_byte() > declaration.end_byte())
12877    {
12878        return None;
12879    }
12880    let declarator = declaration.child_by_field_name("declarator")?;
12881    let mut error_end = None;
12882    let mut names = Vec::new();
12883    let mut stack = vec![declarator];
12884    while let Some(node) = stack.pop() {
12885        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
12886            error_end =
12887                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
12888            continue;
12889        }
12890        if matches!(node.kind(), "identifier" | "type_identifier") {
12891            names.push(node.start_byte());
12892        }
12893        push_named_children_reversed(node, &mut stack);
12894    }
12895    let error_end = error_end?;
12896    names
12897        .into_iter()
12898        .any(|start| start >= error_end)
12899        .then_some(declaration)
12900}
12901
12902fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
12903    let Some(error) = parent.child(error_index) else {
12904        return false;
12905    };
12906    if error.kind() != "ERROR"
12907        || error.child_count() != 1
12908        || error.child(0).is_none_or(|child| child.kind() != "}")
12909    {
12910        return false;
12911    }
12912    let Some(semicolon) = parent.child(error_index + 1) else {
12913        return false;
12914    };
12915    semicolon.kind() == "expression_statement"
12916        && semicolon.child_count() == 1
12917        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
12918}
12919
12920/// Locate the real end of a class-like declaration when a macro invocation
12921/// without a source semicolon absorbs the class's `};` into its parsed field.
12922/// The grammar then keeps following namespace declarations as later children
12923/// of the same field list. The direct ERROR-plus-semicolon pair proves the
12924/// boundary structurally; no source-text delimiter scan is needed.
12925fn displaced_macro_class_tail(
12926    declaration_node: Node<'_>,
12927    body: Node<'_>,
12928    source: &str,
12929) -> Option<DisplacedMacroClassTail> {
12930    if !matches!(
12931        declaration_node.kind(),
12932        "class_specifier" | "struct_specifier" | "union_specifier"
12933    ) || body.kind() != "field_declaration_list"
12934    {
12935        return None;
12936    }
12937
12938    let child_count = body.named_child_count();
12939    for index in 0..child_count {
12940        let child = body.named_child(index)?;
12941        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
12942            continue;
12943        };
12944        let split_index = index + 1;
12945        if split_index >= child_count {
12946            return None;
12947        }
12948        let mut cursor = body.walk();
12949        if !body
12950            .named_children(&mut cursor)
12951            .skip(split_index)
12952            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
12953        {
12954            return None;
12955        }
12956        return Some(DisplacedMacroClassTail {
12957            split_index,
12958            class_range: Range {
12959                start_byte: declaration_node.start_byte(),
12960                end_byte: terminator.end_byte(),
12961                start_line: declaration_node.start_position().row + 1,
12962                end_line: terminator.end_position().row + 1,
12963            },
12964        });
12965    }
12966    None
12967}
12968
12969fn displaced_macro_field_terminator<'tree>(
12970    field: Node<'tree>,
12971    source: &str,
12972) -> Option<Node<'tree>> {
12973    if field.kind() != "field_declaration" {
12974        return None;
12975    }
12976    let macro_type = field.child_by_field_name("type")?;
12977    if macro_type.kind() != "type_identifier"
12978        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12979        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
12980    {
12981        return None;
12982    }
12983    for index in 0..field.child_count() {
12984        let error = field.child(index)?;
12985        if error.kind() != "ERROR"
12986            || error.child_count() != 1
12987            || error.child(0).is_none_or(|child| child.kind() != "}")
12988        {
12989            continue;
12990        }
12991        let semicolon = field.child(index + 1)?;
12992        if semicolon.kind() == ";" {
12993            return Some(semicolon);
12994        }
12995    }
12996    None
12997}
12998
12999fn recover_fragmented_partial_specialization<'tree>(
13000    template_node: Node<'tree>,
13001    declaration_child: Node<'tree>,
13002    source: &str,
13003    ancestry: &ParentIndex<'tree>,
13004) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
13005    if declaration_child.kind() != "function_definition" {
13006        return None;
13007    }
13008    let class_node = declaration_child.child_by_field_name("type")?;
13009    if !matches!(
13010        class_node.kind(),
13011        "class_specifier" | "struct_specifier" | "union_specifier"
13012    ) || !class_node
13013        .child_by_field_name("name")
13014        .and_then(|name| direct_identifier_name(name, source))
13015        .is_some_and(|name| cpp_export_macro_token(&name))
13016    {
13017        return None;
13018    }
13019    let declarator = declaration_child.child_by_field_name("declarator")?;
13020    if declarator.kind() != "template_function" {
13021        return None;
13022    }
13023    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
13024    if metadata.specialization_arguments.is_empty() {
13025        return None;
13026    }
13027    let body = declaration_child.child_by_field_name("body")?;
13028    if body.kind() != "compound_statement" {
13029        return None;
13030    }
13031    let complete_prefix = body.named_child(0).filter(|first| {
13032        first.kind() == "labeled_statement"
13033            && first.has_error()
13034            && first
13035                .named_child(first.named_child_count().saturating_sub(1))
13036                .is_some_and(recovered_declaration_has_class_terminator)
13037    });
13038    let complete_body = complete_prefix.is_some();
13039    let mut prefix_members = Vec::new();
13040    if let Some(prefix) = complete_prefix {
13041        prefix_members.push(prefix);
13042    } else {
13043        let mut body_cursor = body.walk();
13044        for child in body.named_children(&mut body_cursor) {
13045            if !is_structurally_valid_fragmented_class_prefix_member(child) {
13046                break;
13047            }
13048            prefix_members.push(child);
13049        }
13050    }
13051    let containing_declarations = template_node.parent()?;
13052    if !matches!(
13053        containing_declarations.kind(),
13054        "declaration_list" | "compound_statement"
13055    ) {
13056        return None;
13057    }
13058    let mut member_siblings = Vec::new();
13059    let mut following_declarations = Vec::new();
13060    let terminator;
13061    if complete_body {
13062        terminator = complete_prefix?;
13063        let mut cursor = body.walk();
13064        let mut after_prefix = false;
13065        for child in body.named_children(&mut cursor) {
13066            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
13067                after_prefix = true;
13068            } else if after_prefix {
13069                following_declarations.push(child);
13070            }
13071        }
13072    } else {
13073        let mut found_template = false;
13074        let mut cursor = containing_declarations.walk();
13075        let mut class_terminator = None;
13076        for child in containing_declarations.children(&mut cursor) {
13077            if same_node(child, template_node) {
13078                found_template = true;
13079                continue;
13080            }
13081            if found_template && child.kind() == "}" {
13082                class_terminator = Some(child);
13083                break;
13084            }
13085            // A namespace can never be a class member: reaching one before the
13086            // terminator proves the class's true close was swallowed upstream
13087            // and this scan has crossed into the enclosing scope, so the
13088            // recovery cannot be bounded -- continuing re-owns the namespace
13089            // block (and its template specializations) as class members under
13090            // a re-appended package, desyncing the fq boundary (#2306).
13091            if found_template && child.kind() == "namespace_definition" {
13092                return None;
13093            }
13094            if found_template && child.is_named() {
13095                member_siblings.push(child);
13096            }
13097        }
13098        terminator = class_terminator?;
13099    }
13100    let name = format!(
13101        "{}<{}>",
13102        metadata.primary_name,
13103        metadata
13104            .specialization_arguments
13105            .iter()
13106            .map(|argument| argument.text.as_str())
13107            .collect::<Vec<_>>()
13108            .join(", ")
13109    );
13110    Some(RecoveredFragmentedPartialSpecialization {
13111        declaration_node: declaration_child,
13112        name,
13113        range: Range {
13114            start_byte: declaration_child.start_byte(),
13115            end_byte: terminator.end_byte(),
13116            start_line: declaration_child.start_position().row + 1,
13117            end_line: terminator.end_position().row + 1,
13118        },
13119        prefix_members,
13120        member_siblings,
13121        following_declarations,
13122    })
13123}
13124
13125/// Whether `node` is the function-shaped parser wrapper whose body was
13126/// recovered as a fragmented partial-specialization class body.
13127///
13128/// Consumers that walk lexical function scopes must use the same complete
13129/// structural proof as declaration extraction. Otherwise member callables in
13130/// the recovered body are mistaken for function-local declarations.
13131pub fn is_recovered_fragmented_partial_specialization_container(
13132    node: Node<'_>,
13133    source: &str,
13134) -> bool {
13135    let Some(template) = node
13136        .parent()
13137        .filter(|parent| parent.kind() == "template_declaration")
13138    else {
13139        return false;
13140    };
13141    let mut root = template;
13142    while let Some(parent) = root.parent() {
13143        root = parent;
13144    }
13145    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
13146        .is_some()
13147}
13148
13149fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
13150    if declaration.kind() != "declaration" {
13151        return false;
13152    }
13153    // With an export macro between `class` and its name, tree-sitter folds a
13154    // complete class body into a function-shaped declaration. The class's own
13155    // `};` remains structurally identifiable as a direct ERROR child holding
13156    // `}`, immediately followed by the declaration's direct `;` child.
13157    (0..declaration.child_count().saturating_sub(1)).any(|index| {
13158        let Some(error) = declaration.child(index) else {
13159            return false;
13160        };
13161        error.kind() == "ERROR"
13162            && error.child_count() == 1
13163            && error.child(0).is_some_and(|child| child.kind() == "}")
13164            && declaration
13165                .child(index + 1)
13166                .is_some_and(|child| child.kind() == ";")
13167    })
13168}
13169
13170fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
13171    if node.has_error() {
13172        return false;
13173    }
13174    match node.kind() {
13175        "declaration"
13176        | "field_declaration"
13177        | "alias_declaration"
13178        | "type_definition"
13179        | "static_assert_declaration" => true,
13180        "labeled_statement" => node
13181            .named_child(node.named_child_count().saturating_sub(1))
13182            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
13183        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
13184            matches!(
13185                child.kind(),
13186                "declaration"
13187                    | "field_declaration"
13188                    | "alias_declaration"
13189                    | "type_definition"
13190                    | "function_definition"
13191            )
13192        }),
13193        _ => false,
13194    }
13195}
13196
13197fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
13198    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
13199        .then(|| node.child_by_field_name("declarator"))
13200        .flatten()
13201        .and_then(|declarator| extract_variable_name(declarator, source))
13202}
13203
13204use crate::graph::syntax::has_function_scope_ancestor;
13205
13206fn cpp_template_metadata<'tree>(
13207    template_node: Node<'tree>,
13208    declaration_child: Node<'tree>,
13209    source: &str,
13210    ancestry: &ParentIndex<'tree>,
13211) -> Option<CppTemplateMetadata> {
13212    let parameters_node = template_node.child_by_field_name("parameters")?;
13213    let name_node = cpp_templated_class_name_node(declaration_child)?;
13214    let primary_node = match name_node.kind() {
13215        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
13216        _ => name_node,
13217    };
13218    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
13219    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
13220        return None;
13221    }
13222
13223    let mut parameter_nodes = Vec::new();
13224    let mut parameter_names = Vec::new();
13225    let mut cursor = parameters_node.walk();
13226    for parameter in parameters_node.named_children(&mut cursor) {
13227        if !matches!(
13228            parameter.kind(),
13229            "type_parameter_declaration"
13230                | "optional_type_parameter_declaration"
13231                | "variadic_type_parameter_declaration"
13232                | "template_template_parameter_declaration"
13233                | "parameter_declaration"
13234                | "optional_parameter_declaration"
13235                | "variadic_parameter_declaration"
13236        ) {
13237            continue;
13238        }
13239        let index = parameter_nodes.len();
13240        // An unnamed parameter still contributes template arity and kind. Use
13241        // an impossible C++ identifier so positional reconciliation can bind
13242        // it without making source expressions refer to a name that was not
13243        // written.
13244        let name = cpp_template_parameter_name(parameter, source)
13245            .unwrap_or_else(|| format!("<anonymous:{index}>"));
13246        parameter_names.push(name);
13247        parameter_nodes.push(parameter);
13248    }
13249    let parameters = parameter_nodes
13250        .into_iter()
13251        .zip(parameter_names.iter().cloned())
13252        .map(|(parameter, name)| CppTemplateParameterMetadata {
13253            name,
13254            kind: cpp_template_parameter_kind(parameter),
13255            variadic: matches!(
13256                parameter.kind(),
13257                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
13258            ),
13259            default: cpp_template_parameter_default_expression(
13260                parameter,
13261                source,
13262                &parameter_names,
13263                ancestry,
13264            ),
13265        })
13266        .collect();
13267    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
13268        Vec::new()
13269    } else {
13270        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
13271            .unwrap_or_default()
13272    };
13273    let alias_target = (declaration_child.kind() == "alias_declaration")
13274        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
13275        .flatten();
13276    Some(CppTemplateMetadata {
13277        primary_name,
13278        primary_fq_name: String::new(),
13279        parameters,
13280        specialization_arguments,
13281        alias_target,
13282    })
13283}
13284
13285fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
13286    match node.kind() {
13287        "class_specifier" | "struct_specifier" | "union_specifier" => {
13288            node.child_by_field_name("name")
13289        }
13290        "function_definition" => {
13291            let declarator = node.child_by_field_name("declarator")?;
13292            if matches!(declarator.kind(), "identifier" | "template_function") {
13293                Some(declarator)
13294            } else {
13295                None
13296            }
13297        }
13298        "alias_declaration" => node.child_by_field_name("name"),
13299        _ => None,
13300    }
13301}
13302
13303fn cpp_template_alias_target<'tree>(
13304    alias: Node<'tree>,
13305    source: &str,
13306    parameter_names: &[String],
13307    ancestry: &ParentIndex<'tree>,
13308) -> Option<CppTemplateAliasTargetMetadata> {
13309    let mut type_node = alias.child_by_field_name("type")?;
13310    while type_node.kind() == "type_descriptor" {
13311        type_node = type_node.child_by_field_name("type")?;
13312    }
13313    let global = type_node.child_by_field_name("scope").is_none()
13314        && type_node.child(0).is_some_and(|child| child.kind() == "::");
13315    let mut components = Vec::new();
13316    cpp_template_target_components(type_node, source, &mut components)?;
13317    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
13318    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
13319        components,
13320        global,
13321        arguments,
13322    })
13323}
13324
13325fn cpp_template_target_components(
13326    node: Node<'_>,
13327    source: &str,
13328    out: &mut Vec<String>,
13329) -> Option<()> {
13330    match node.kind() {
13331        "identifier" | "namespace_identifier" | "type_identifier" => {
13332            out.push(node_text(node, source).to_string());
13333            Some(())
13334        }
13335        "template_type" => {
13336            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13337        }
13338        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13339            if let Some(scope) = node.child_by_field_name("scope") {
13340                cpp_template_target_components(scope, source, out)?;
13341            }
13342            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
13343        }
13344        _ => None,
13345    }
13346}
13347
13348fn cpp_template_argument_expressions<'tree>(
13349    mut node: Node<'tree>,
13350    source: &str,
13351    parameter_names: &[String],
13352    ancestry: &ParentIndex<'tree>,
13353) -> Option<Vec<CppTemplateExpression>> {
13354    loop {
13355        match node.kind() {
13356            "template_type" | "template_function" => {
13357                let arguments = node.child_by_field_name("arguments")?;
13358                let mut cursor = arguments.walk();
13359                return Some(
13360                    arguments
13361                        .named_children(&mut cursor)
13362                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
13363                        .map(|argument| {
13364                            cpp_template_expression(argument, source, parameter_names, ancestry)
13365                        })
13366                        .collect(),
13367                );
13368            }
13369            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
13370                node = node
13371                    .child_by_field_name("name")
13372                    .or_else(|| node.child_by_field_name("type"))?;
13373            }
13374            _ => return None,
13375        }
13376    }
13377}
13378
13379fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
13380    let candidate = node
13381        .child_by_field_name("name")
13382        .or_else(|| node.child_by_field_name("declarator"))
13383        .or_else(|| {
13384            let mut cursor = node.walk();
13385            node.named_children(&mut cursor).find(|child| {
13386                matches!(
13387                    child.kind(),
13388                    "identifier" | "type_identifier" | "field_identifier"
13389                )
13390            })
13391        })?;
13392    if let Some(name) = direct_identifier_name(candidate, source) {
13393        return Some(name);
13394    }
13395    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
13396    (!name.is_empty()).then_some(name)
13397}
13398
13399fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
13400    match node.kind() {
13401        "type_parameter_declaration"
13402        | "optional_type_parameter_declaration"
13403        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
13404        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
13405        _ => CppTemplateParameterKind::Value,
13406    }
13407}
13408
13409#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
13410pub struct CppTemplateParameterNameMatches {
13411    pub any_parameter: bool,
13412    pub type_parameter: bool,
13413}
13414
13415pub fn cpp_template_parameter_name_matches(
13416    parameters: Node<'_>,
13417    name: &str,
13418    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
13419) -> CppTemplateParameterNameMatches {
13420    let mut matches = CppTemplateParameterNameMatches::default();
13421    let mut cursor = parameters.walk();
13422    for parameter in parameters.named_children(&mut cursor) {
13423        if parameter_name(parameter).as_deref() == Some(name) {
13424            matches.any_parameter = true;
13425            matches.type_parameter |=
13426                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
13427        }
13428    }
13429    matches
13430}
13431
13432pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
13433    parent.child_by_field_name("type").is_some_and(|type_node| {
13434        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
13435    })
13436}
13437
13438fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
13439    node.child_by_field_name("default_type")
13440        .or_else(|| node.child_by_field_name("default_value"))
13441}
13442
13443fn cpp_template_parameter_default_expression<'tree>(
13444    parameter: Node<'tree>,
13445    source: &str,
13446    parameter_names: &[String],
13447    ancestry: &ParentIndex<'tree>,
13448) -> Option<CppTemplateExpression> {
13449    let default = cpp_template_parameter_default(parameter)?;
13450    let base = cpp_template_expression(default, source, parameter_names, ancestry);
13451    let Some(pointer_error) = parameter.next_named_sibling() else {
13452        return Some(base);
13453    };
13454    let Some(pointer_declarator) =
13455        recovered_abstract_pointer_declarator_term(pointer_error, source)
13456    else {
13457        return Some(base);
13458    };
13459    Some(CppTemplateExpression {
13460        text: format!(
13461            "{}{}",
13462            base.text,
13463            normalize_cpp_whitespace(node_text(pointer_error, source))
13464        ),
13465        term: CppTemplateTerm::Node {
13466            kind: "type_descriptor".to_string(),
13467            children: vec![base.term, pointer_declarator],
13468        },
13469    })
13470}
13471
13472fn recovered_abstract_pointer_declarator_term(
13473    node: Node<'_>,
13474    source: &str,
13475) -> Option<CppTemplateTerm> {
13476    if node.kind() != "ERROR" || node.child_count() == 0 {
13477        return None;
13478    }
13479    let mut children = Vec::new();
13480    for index in 0..node.child_count() {
13481        let child = node.child(index)?;
13482        if child.kind() != "*" {
13483            return None;
13484        }
13485        children.push(CppTemplateTerm::Atom {
13486            kind: "*".to_string(),
13487            text: normalize_cpp_whitespace(node_text(child, source)),
13488        });
13489    }
13490    Some(CppTemplateTerm::Node {
13491        kind: "abstract_pointer_declarator".to_string(),
13492        children,
13493    })
13494}
13495
13496fn cpp_template_expression<'tree>(
13497    node: Node<'tree>,
13498    source: &str,
13499    parameter_names: &[String],
13500    ancestry: &ParentIndex<'tree>,
13501) -> CppTemplateExpression {
13502    let text = normalize_cpp_whitespace(node_text(node, source));
13503    CppTemplateExpression {
13504        text,
13505        term: cpp_template_term(node, source, parameter_names, ancestry),
13506    }
13507}
13508
13509pub fn cpp_template_term<'tree>(
13510    node: Node<'tree>,
13511    source: &str,
13512    parameter_names: &[String],
13513    ancestry: &ParentIndex<'tree>,
13514) -> CppTemplateTerm {
13515    enum Work<'tree> {
13516        Visit(Node<'tree>),
13517        Build { kind: String, child_count: usize },
13518    }
13519
13520    let mut work = vec![Work::Visit(node)];
13521    let mut terms = Vec::new();
13522    while let Some(next) = work.pop() {
13523        match next {
13524            Work::Visit(current) => {
13525                let text = normalize_cpp_whitespace(node_text(current, source));
13526                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
13527                    terms.push(CppTemplateTerm::Parameter(text));
13528                    continue;
13529                }
13530                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
13531                    let mut cursor = current.walk();
13532                    let named = current
13533                        .named_children(&mut cursor)
13534                        .filter(|child| !child.is_extra() && child.kind() != "comment")
13535                        .collect::<Vec<_>>();
13536                    if let [child] = named.as_slice() {
13537                        work.push(Work::Visit(*child));
13538                        continue;
13539                    }
13540                }
13541                if current.child_count() == 0 {
13542                    terms.push(CppTemplateTerm::Atom {
13543                        kind: if matches!(
13544                            current.kind(),
13545                            "identifier"
13546                                | "type_identifier"
13547                                | "field_identifier"
13548                                | "namespace_identifier"
13549                        ) {
13550                            "identifier".to_string()
13551                        } else {
13552                            current.kind().to_string()
13553                        },
13554                        text,
13555                    });
13556                    continue;
13557                }
13558                let children = (0..current.child_count())
13559                    .filter_map(|index| current.child(index))
13560                    .filter(|child| !child.is_extra() && child.kind() != "comment")
13561                    .collect::<Vec<_>>();
13562                work.push(Work::Build {
13563                    kind: current.kind().to_string(),
13564                    child_count: children.len(),
13565                });
13566                work.extend(children.into_iter().rev().map(Work::Visit));
13567            }
13568            Work::Build { kind, child_count } => {
13569                let children = terms.split_off(terms.len() - child_count);
13570                terms.push(CppTemplateTerm::Node { kind, children });
13571            }
13572        }
13573    }
13574    terms.pop().expect("template term traversal emits one root")
13575}
13576
13577fn cpp_template_term_leaf_is_parameter<'tree>(
13578    node: Node<'tree>,
13579    text: &str,
13580    parameter_names: &[String],
13581    ancestry: &ParentIndex<'tree>,
13582) -> bool {
13583    if !parameter_names.iter().any(|parameter| parameter == text) {
13584        return false;
13585    }
13586    !ancestry.parent(node).is_some_and(|parent| {
13587        matches!(
13588            parent.kind(),
13589            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
13590        ) && parent.child_by_field_name("scope").is_some()
13591            && parent.child_by_field_name("name") == Some(node)
13592    })
13593}
13594
13595fn enclosing_cpp_declaration_node<'tree>(
13596    mut node: Node<'tree>,
13597    ancestry: &ParentIndex<'tree>,
13598) -> Option<Node<'tree>> {
13599    loop {
13600        match node.kind() {
13601            "declaration"
13602            | "function_declaration"
13603            | "field_declaration"
13604            | "function_definition" => return Some(node),
13605            _ => node = ancestry.parent(node)?,
13606        }
13607    }
13608}
13609
13610fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
13611    let mut params = Vec::new();
13612    let mut cursor = parameters_node.walk();
13613    for child in parameters_node.children(&mut cursor) {
13614        match child.kind() {
13615            "explicit_object_parameter_declaration" => {
13616                if let Some(parameter) = child
13617                    .named_children(&mut child.walk())
13618                    .find(|node| node.kind() == "parameter_declaration")
13619                {
13620                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
13621                }
13622            }
13623            "parameter_declaration" | "optional_parameter_declaration" => {
13624                params.push(cpp_parameter_type(child, source));
13625            }
13626            "variadic_parameter_declaration" => {
13627                params.push(cpp_parameter_type(child, source));
13628            }
13629            "variadic_parameter" | "..." => params.push("...".to_string()),
13630            _ => {}
13631        }
13632    }
13633
13634    if params.is_empty() {
13635        "()".to_string()
13636    } else {
13637        format!("({})", params.join(", "))
13638    }
13639}
13640
13641/// Where a C or C++ callable declaration is written, which is what decides
13642/// which modifier facts its own node is able to state.
13643enum CppCallableScope<'tree> {
13644    /// Written directly in a class, struct or union body. The declaration
13645    /// states its own storage class, and its access is the one the body's
13646    /// specifier ladder is under at that point.
13647    ClassBody {
13648        body: Node<'tree>,
13649        default_visibility: DeclaredVisibility,
13650    },
13651    /// A qualified declaration written outside every class body
13652    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
13653    /// writes no access specifier, and whether the qualifier names a class or
13654    /// a namespace is not decidable from this file alone -- that is exactly
13655    /// what [`crate::reconcile`] needs the include-visible class table for.
13656    Qualified,
13657    /// Namespace scope or file scope, reached by an unqualified name. C has no
13658    /// member functions at all, so every C callable lands here.
13659    Unqualified,
13660}
13661
13662/// Where the declaration sits, from one upward walk of its own ancestry.
13663///
13664/// The walk stops as soon as something proves the declaration is outside every
13665/// class body: a `namespace_definition`, because a class body never encloses
13666/// one, and a `compound_statement`, because a body opens a block scope and
13667/// nothing declared in it is a member of the class the enclosing definition
13668/// belongs to. That is the same rule
13669/// `CppVisitor::visit_anonymous_local_aggregates_in_function` applies when it
13670/// drops the aggregate carrier, and it keeps this question off the O(depth)
13671/// walk a deeply nested generated header would otherwise pay per callable.
13672///
13673/// `function_declarator` is `None` for a declaration whose callable the macro
13674/// recovery reparsed out of a malformed member: there is no declarator node to
13675/// ask whether the name carries a qualifier, and a recovered member is written
13676/// in its class body anyway.
13677fn cpp_callable_scope<'tree>(
13678    declaration: Node<'tree>,
13679    function_declarator: Option<Node<'tree>>,
13680    ancestry: &ParentIndex<'tree>,
13681) -> CppCallableScope<'tree> {
13682    let mut current = Some(declaration);
13683    while let Some(node) = current {
13684        match node.kind() {
13685            "field_declaration_list" => {
13686                let default_visibility =
13687                    ancestry
13688                        .parent(node)
13689                        .map_or(DeclaredVisibility::Unknown, |class_like| {
13690                            // `struct` and `union` members default to public;
13691                            // `class` members default to private.
13692                            match class_like.kind() {
13693                                "struct_specifier" | "union_specifier" => {
13694                                    DeclaredVisibility::Public
13695                                }
13696                                _ => DeclaredVisibility::Private,
13697                            }
13698                        });
13699                return CppCallableScope::ClassBody {
13700                    body: node,
13701                    default_visibility,
13702                };
13703            }
13704            "namespace_definition" | "compound_statement" | "translation_unit" => break,
13705            _ => {}
13706        }
13707        current = ancestry.parent(node);
13708    }
13709    if cpp_callable_declarator_is_qualified(function_declarator) {
13710        CppCallableScope::Qualified
13711    } else {
13712        CppCallableScope::Unqualified
13713    }
13714}
13715
13716/// Whether the declarator's own written name carries a `::` qualifier.
13717///
13718/// A qualified declaration written outside every class body
13719/// (`long Client::send(int) {}`) names a member whose class body this file may
13720/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
13721/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
13722fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
13723    function_declarator
13724        .and_then(cpp_function_declarator_name_node)
13725        .is_some_and(|name| name.kind() == "qualified_identifier")
13726}
13727
13728/// The access one class-body member declaration is written under.
13729///
13730/// C++ states member access positionally: an `access_specifier` applies to
13731/// every member written after it until the next one, and the members before
13732/// the first one take the class keyword's default. The answer is therefore the
13733/// last specifier the body writes before the member's own list-level node,
13734/// read from the specifier's own keyword token rather than from its text.
13735///
13736/// The body's children are read forward with one cursor rather than backwards
13737/// from the member: `Node::prev_sibling` re-descends from the root for each
13738/// step, which is the per-edge cost #2361 keeps off this walk.
13739fn cpp_member_declared_visibility<'tree>(
13740    declaration: Node<'tree>,
13741    body: Node<'tree>,
13742    default_visibility: DeclaredVisibility,
13743    ancestry: &ParentIndex<'tree>,
13744) -> DeclaredVisibility {
13745    let mut list_level = declaration;
13746    while ancestry.parent(list_level) != Some(body) {
13747        let Some(parent) = ancestry.parent(list_level) else {
13748            return default_visibility;
13749        };
13750        list_level = parent;
13751    }
13752    let mut visibility = default_visibility;
13753    let mut cursor = body.walk();
13754    for child in body.children(&mut cursor) {
13755        if child == list_level {
13756            break;
13757        }
13758        if child.kind() == "access_specifier" {
13759            visibility = match child.child(0).map(|keyword| keyword.kind()) {
13760                Some("public") => DeclaredVisibility::Public,
13761                Some("protected") => DeclaredVisibility::Protected,
13762                Some("private") => DeclaredVisibility::Private,
13763                _ => visibility,
13764            };
13765        }
13766    }
13767    visibility
13768}
13769
13770/// Whether this declaration writes the `static` storage class specifier.
13771///
13772/// What `static` *means* is the scope's business: on a class-body member it is
13773/// the receiver fact, and at file scope it is internal linkage, which is why
13774/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
13775/// question and answer it differently.
13776fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
13777    let mut cursor = declaration.walk();
13778    declaration.named_children(&mut cursor).any(|child| {
13779        child.kind() == "storage_class_specifier"
13780            && normalize_cpp_whitespace(node_text(child, source)) == "static"
13781    })
13782}
13783
13784/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
13785/// publishes, as C and C++ state them.
13786struct CppCallableModifiers {
13787    is_static: bool,
13788    is_constructor: bool,
13789    visibility: DeclaredVisibility,
13790}
13791
13792/// The modifier facts a C or C++ callable declaration states in its own shape.
13793///
13794/// C++ has no `static` keyword for receivers and no visibility keyword of the
13795/// Java kind: it states both in the declaration's position. A member declared
13796/// in a class body binds the object it is selected on unless that declaration
13797/// writes the `static` storage class, and its access is the one the body's
13798/// specifier ladder is under. A function at namespace or file scope binds
13799/// nothing and has no access specifier at all, so its declared visibility is
13800/// Unknown. Linkage is recorded separately and does not declare access. C has
13801/// no member functions, and the `static` a C function writes is internal linkage,
13802/// never a receiver fact.
13803///
13804/// Recording these is what lets `receiver_contract_of` answer for a C or C++
13805/// declaration. Without them it reported no contract at all,
13806/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
13807/// callable, no reviewed summary could name one, and every effect or taint walk
13808/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
13809/// in #3455, Kotlin in #3453, Python in #3451.
13810///
13811/// An out-of-line member definition cannot repeat `static` or access. These
13812/// are per-file facts only: the analyzer reconciles the exact overload with
13813/// include-visible class declarations before exposing its receiver contract
13814/// (#3509). A missing declaration leaves that contract unknown.
13815fn cpp_callable_modifiers<'tree>(
13816    declaration: Node<'tree>,
13817    function_declarator: Option<Node<'tree>>,
13818    is_constructor: bool,
13819    source: &str,
13820    ancestry: &ParentIndex<'tree>,
13821) -> CppCallableModifiers {
13822    match cpp_callable_scope(declaration, function_declarator, ancestry) {
13823        CppCallableScope::ClassBody {
13824            body,
13825            default_visibility,
13826        } => CppCallableModifiers {
13827            is_static: cpp_declares_static_storage_class(declaration, source),
13828            is_constructor,
13829            visibility: cpp_member_declared_visibility(
13830                declaration,
13831                body,
13832                default_visibility,
13833                ancestry,
13834            ),
13835        },
13836        CppCallableScope::Qualified => CppCallableModifiers {
13837            is_static: false,
13838            is_constructor,
13839            visibility: DeclaredVisibility::Unknown,
13840        },
13841        CppCallableScope::Unqualified => CppCallableModifiers {
13842            is_static: false,
13843            is_constructor,
13844            visibility: DeclaredVisibility::Unknown,
13845        },
13846    }
13847}
13848
13849/// The persisted signature facts one C or C++ callable declaration publishes.
13850///
13851/// `linkage` is the caller's, not this function's: the node that carries the
13852/// storage class is not always the declaration this declarator sits in -- a
13853/// recovered constructor reads it from its class declaration, and a
13854/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
13855/// visit site that knows which node states it passes the answer in. Recording
13856/// it here keeps it beside the declared visibility it also decides.
13857fn cpp_signature_metadata<'tree>(
13858    signature: String,
13859    function_declarator: Node<'tree>,
13860    linkage: CallableLinkage,
13861    source: &str,
13862    ancestry: &ParentIndex<'tree>,
13863) -> SignatureMetadata {
13864    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
13865    let is_constructor =
13866        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
13867    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
13868        Some(declaration) => cpp_callable_modifiers(
13869            declaration,
13870            Some(function_declarator),
13871            is_constructor,
13872            source,
13873            ancestry,
13874        ),
13875        // Severe recovery left no declaration node above this declarator, so
13876        // nothing is stated here beyond what the callable's own name proves.
13877        None => CppCallableModifiers {
13878            is_static: false,
13879            is_constructor,
13880            visibility: DeclaredVisibility::Unknown,
13881        },
13882    };
13883    let enrich = |mut metadata: SignatureMetadata| {
13884        // #3507: a qualified definition repeats none of the class body's
13885        // dispatch facts, so this row publishes none either.
13886        if let Some(dispatch) = dispatch {
13887            metadata = metadata.with_dispatch_extensibility(dispatch);
13888        }
13889        metadata
13890            .with_callable_linkage(linkage)
13891            .with_callable_modifiers(
13892                modifiers.is_static,
13893                modifiers.is_constructor,
13894                modifiers.visibility,
13895            )
13896    };
13897    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
13898    let return_type_identity =
13899        cpp_callable_return_type_identity(function_declarator, source, ancestry);
13900    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13901        return enrich(
13902            SignatureMetadata::new(signature, Vec::new())
13903                .with_return_type_text(return_type_text)
13904                .with_return_type_identity(return_type_identity),
13905        );
13906    };
13907    let callable_arity = cpp_callable_arity(parameters_node, source);
13908    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
13909    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
13910    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
13911    let Some(relative_start) = signature
13912        .get(search_from..)
13913        .and_then(|suffix| suffix.find(&parameter_text))
13914    else {
13915        return enrich(
13916            SignatureMetadata::new(signature, Vec::new())
13917                .with_callable_arity(callable_arity)
13918                .with_callable_parameter_types(callable_parameter_types)
13919                .with_return_type_text(return_type_text)
13920                .with_return_type_identity(return_type_identity),
13921        );
13922    };
13923    let parameters_start = search_from + relative_start;
13924    let parameters_end = parameters_start + parameter_text.len();
13925    let mut search_start = parameters_start;
13926    let parameters = cpp_parameter_label_nodes(parameters_node)
13927        .into_iter()
13928        .filter_map(|label_node| {
13929            let label = normalize_cpp_whitespace(node_text(label_node, source));
13930            if label.is_empty() || search_start > parameters_end {
13931                return None;
13932            }
13933            let haystack = signature.get(search_start..parameters_end)?;
13934            let relative_start = haystack.find(&label)?;
13935            let start_byte = search_start + relative_start;
13936            let end_byte = start_byte + label.len();
13937            search_start = end_byte;
13938            Some(ParameterMetadata::new(label, start_byte, end_byte))
13939        })
13940        .collect();
13941    enrich(
13942        SignatureMetadata::new(signature, parameters)
13943            .with_callable_arity(callable_arity)
13944            .with_callable_parameter_types(callable_parameter_types)
13945            .with_return_type_text(return_type_text)
13946            .with_return_type_identity(return_type_identity),
13947    )
13948}
13949
13950pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
13951    function_declarator: Node<'tree>,
13952    source: &str,
13953    ancestry: &ParentIndex<'tree>,
13954) -> bool {
13955    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
13956        .or_else(|| function_declarator.child_by_field_name("name"))
13957        .or_else(|| last_named_child(function_declarator))
13958    else {
13959        return false;
13960    };
13961    // An out-of-line definition writes the class in the name itself
13962    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
13963    // terminal component answers without any enclosing class body.
13964    if name_node.kind() == "qualified_identifier" {
13965        let owner = name_node
13966            .child_by_field_name("scope")
13967            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
13968        let member = name_node
13969            .child_by_field_name("name")
13970            .and_then(|name| canonical_cpp_qualified_component(name, source));
13971        return match (owner, member) {
13972            (Some(owner), Some(member)) => owner.name == member.name,
13973            _ => false,
13974        };
13975    }
13976    let Some(callable_name) = direct_identifier_name(name_node, source) else {
13977        return false;
13978    };
13979
13980    let mut current = ancestry.parent(function_declarator);
13981    while let Some(ancestor) = current {
13982        let owner_name = match ancestor.kind() {
13983            "class_specifier" | "struct_specifier" | "union_specifier" => {
13984                class_like_name(ancestor, source, ancestry)
13985            }
13986            "ERROR" => malformed_class_error_owner_name(ancestor, source),
13987            _ => None,
13988        };
13989        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
13990            return true;
13991        }
13992        current = ancestry.parent(ancestor);
13993    }
13994    false
13995}
13996
13997/// Recover the owner name from the direct grammar shape retained when a later
13998/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
13999/// `ERROR` node:
14000///
14001/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
14002///
14003/// Direct-child checks keep this distinct from an unrelated nested class inside
14004/// a broader error region. The closing brace may be displaced past the error
14005/// node, so the opening body token is the available structural boundary.
14006fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
14007    if node.kind() != "ERROR" {
14008        return None;
14009    }
14010    let keyword = node.child(0)?;
14011    if !matches!(keyword.kind(), "class" | "struct" | "union") {
14012        return None;
14013    }
14014    let name_node = node.child(1)?;
14015    let name = direct_identifier_name(name_node, source)?;
14016    let has_body = (2..node.child_count())
14017        .filter_map(|index| node.child(index))
14018        .any(|child| child.kind() == "{");
14019    has_body.then_some(name)
14020}
14021
14022/// Whether one declaration carries a native must-use attribute.
14023///
14024/// Attribute names are read only from structurally selected attribute nodes;
14025/// an identifier with the same spelling elsewhere in the declaration cannot
14026/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
14027/// while standard `nodiscard` is accepted without a namespace prefix.
14028pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
14029    // The C++ grammar keeps a prefix attribute on the enclosing declaration
14030    // while the enum unit's occurrence points at its `enum_specifier`. Bring
14031    // that one structural sibling into the same declaration boundary; this
14032    // does not widen the walk to surrounding declarations.
14033    let declaration = declaration
14034        .parent()
14035        .filter(|parent| {
14036            parent.kind() == "declaration"
14037                && parent
14038                    .child_by_field_name("type")
14039                    .is_some_and(|type_node| type_node.id() == declaration.id())
14040        })
14041        .unwrap_or(declaration);
14042    let mut pending = vec![declaration];
14043    while let Some(node) = pending.pop() {
14044        if cpp_attribute_node_is_native_must_use(node, source)
14045            && cpp_attribute_is_attached_to_declaration(node, declaration)
14046        {
14047            return true;
14048        }
14049        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
14050        {
14051            continue;
14052        }
14053        push_named_children_reversed(node, &mut pending);
14054    }
14055    false
14056}
14057
14058fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
14059    if node.kind() == "attribute" {
14060        return node.child_by_field_name("name").is_some_and(|name| {
14061            let name = node_text(name, source);
14062            let prefix = node
14063                .child_by_field_name("prefix")
14064                .map(|prefix| node_text(prefix, source));
14065            matches!(
14066                (prefix, name),
14067                (None, "nodiscard")
14068                    | (None, "warn_unused_result")
14069                    | (Some("gnu"), "warn_unused_result")
14070            )
14071        });
14072    }
14073    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
14074    {
14075        return false;
14076    }
14077    let Some(arguments) = node.named_child(0) else {
14078        return false;
14079    };
14080    let Some(name) = arguments.named_child(0) else {
14081        return false;
14082    };
14083    arguments.kind() == "argument_list"
14084        && name.kind() == "identifier"
14085        && node_text(name, source) == "warn_unused_result"
14086}
14087
14088fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
14089    let mut current = attribute;
14090    while let Some(parent) = current.parent() {
14091        if parent.id() == declaration.id() {
14092            return true;
14093        }
14094        if matches!(
14095            parent.kind(),
14096            "declaration"
14097                | "field_declaration"
14098                | "function_definition"
14099                | "struct_specifier"
14100                | "class_specifier"
14101                | "union_specifier"
14102                | "enum_specifier"
14103        ) {
14104            return false;
14105        }
14106        current = parent;
14107    }
14108    false
14109}
14110
14111fn cpp_callable_return_type_parts<'tree>(
14112    function_declarator: Node<'tree>,
14113    source: &str,
14114    ancestry: &ParentIndex<'tree>,
14115) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
14116    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14117        return None;
14118    }
14119    if let Some((return_type, _)) =
14120        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14121    {
14122        return Some((return_type, Vec::new()));
14123    }
14124    let mut cursor = function_declarator.walk();
14125    if let Some(trailing) = function_declarator
14126        .named_children(&mut cursor)
14127        .find(|child| child.kind() == "trailing_return_type")
14128        && let Some(type_descriptor) = trailing.named_child(0)
14129    {
14130        return Some((type_descriptor, Vec::new()));
14131    }
14132
14133    let mut current = function_declarator;
14134    let mut wrappers = Vec::new();
14135    while let Some(parent) = ancestry.parent(current) {
14136        if matches!(
14137            parent.kind(),
14138            "function_definition" | "declaration" | "field_declaration"
14139        ) {
14140            let type_node = parent.child_by_field_name("type")?;
14141            if cpp_export_macro_token(node_text(type_node, source))
14142                && (0..parent.named_child_count()).any(|index| {
14143                    parent
14144                        .named_child(index)
14145                        .is_some_and(|child| child.kind() == "ERROR")
14146                })
14147            {
14148                return None;
14149            }
14150            return Some((type_node, wrappers));
14151        }
14152        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14153            || (matches!(
14154                parent.kind(),
14155                "pointer_declarator"
14156                    | "reference_declarator"
14157                    | "array_declarator"
14158                    | "parenthesized_declarator"
14159            ) && parent.named_child_count() == 1
14160                && parent.named_child(0) == Some(current));
14161        if !wraps_current_declarator {
14162            return None;
14163        }
14164        match parent.kind() {
14165            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
14166            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
14167            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
14168            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
14169            _ => return None,
14170        }
14171        current = parent;
14172    }
14173    None
14174}
14175
14176/// Return the AST node that describes one callable's declared result type.
14177///
14178/// The node is returned only after the same declarator walk that establishes
14179/// the persisted C++ return identity. Consumers can therefore resolve a
14180/// nominal result or inspect its declaration attributes without parsing a
14181/// rendered signature.
14182pub fn cpp_callable_declaration_return_type_node<'tree>(
14183    callable: Node<'tree>,
14184    source: &str,
14185    ancestry: &ParentIndex<'tree>,
14186) -> Option<Node<'tree>> {
14187    let declarator = callable
14188        .child_by_field_name("declarator")
14189        .and_then(extract_function_declarator)?;
14190    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
14191}
14192
14193/// Return the structured declared result type of one callable declaration.
14194///
14195/// This accepts the enclosing declaration node so consumers do not need to
14196/// duplicate the declarator-unwrapping rules before asking for the structured
14197/// identity.
14198pub fn cpp_callable_declaration_return_type_identity<'tree>(
14199    callable: Node<'tree>,
14200    source: &str,
14201    ancestry: &ParentIndex<'tree>,
14202) -> Option<StructuredTypeIdentity> {
14203    let declarator = callable
14204        .child_by_field_name("declarator")
14205        .and_then(extract_function_declarator)?;
14206    cpp_callable_return_type_identity(declarator, source, ancestry)
14207}
14208
14209pub(crate) fn cpp_callable_return_type_identity<'tree>(
14210    function_declarator: Node<'tree>,
14211    source: &str,
14212    ancestry: &ParentIndex<'tree>,
14213) -> Option<StructuredTypeIdentity> {
14214    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14215    let (return_type, wrappers) =
14216        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
14217    cpp_apply_structured_type_wrappers(
14218        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
14219        wrappers,
14220    )
14221}
14222
14223fn cpp_structured_type_identity(
14224    node: Node<'_>,
14225    source: &str,
14226    lexical_scope: &[String],
14227) -> Option<StructuredTypeIdentity> {
14228    enum Work<'tree> {
14229        Visit(Node<'tree>),
14230        Wrap(CppStructuredTypeWrapper),
14231        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
14232        BuildGeneric { argument_count: usize },
14233    }
14234
14235    let mut work = vec![Work::Visit(node)];
14236    let mut values = Vec::new();
14237    let mut builder = StructuredTypeIdentityBuilder::default();
14238    while let Some(next) = work.pop() {
14239        match next {
14240            Work::Visit(current) => match current.kind() {
14241                "type_descriptor" => {
14242                    let type_node = current
14243                        .child_by_field_name("type")
14244                        .or_else(|| current.named_child(0))?;
14245                    let mut wrappers = Vec::new();
14246                    let mut cursor = current.walk();
14247                    for child in current.named_children(&mut cursor) {
14248                        if child.id() != type_node.id() {
14249                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
14250                        }
14251                    }
14252                    work.push(Work::ApplyWrappers(wrappers));
14253                    work.push(Work::Visit(type_node));
14254                }
14255                "pointer_declarator" | "abstract_pointer_declarator" => {
14256                    let child = current
14257                        .child_by_field_name("declarator")
14258                        .or_else(|| current.named_child(0))?;
14259                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
14260                    work.push(Work::Visit(child));
14261                }
14262                "reference_declarator" => {
14263                    let child = current
14264                        .child_by_field_name("declarator")
14265                        .or_else(|| current.named_child(0))?;
14266                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
14267                    work.push(Work::Visit(child));
14268                }
14269                "array_declarator" | "abstract_array_declarator" => {
14270                    let child = current
14271                        .child_by_field_name("declarator")
14272                        .or_else(|| current.named_child(0))?;
14273                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
14274                    work.push(Work::Visit(child));
14275                }
14276                "template_type" => {
14277                    let name_node = current.child_by_field_name("name")?;
14278                    let arguments = current
14279                        .child_by_field_name("arguments")
14280                        .map(|arguments_node| {
14281                            let mut cursor = arguments_node.walk();
14282                            arguments_node
14283                                .named_children(&mut cursor)
14284                                .filter(|child| !child.is_extra() && child.kind() != "comment")
14285                                .collect::<Vec<_>>()
14286                        })
14287                        .unwrap_or_default();
14288                    work.push(Work::BuildGeneric {
14289                        argument_count: arguments.len(),
14290                    });
14291                    work.extend(arguments.into_iter().rev().map(Work::Visit));
14292                    work.push(Work::Visit(name_node));
14293                }
14294                "qualified_identifier"
14295                | "scoped_identifier"
14296                | "scoped_type_identifier"
14297                | "type_identifier"
14298                | "field_identifier"
14299                | "identifier"
14300                | "namespace_identifier"
14301                | "primitive_type" => {
14302                    values.push(builder.named(cpp_structured_named_type(
14303                        current,
14304                        source,
14305                        lexical_scope,
14306                    )?)?);
14307                }
14308                _ => {
14309                    let child = current.child_by_field_name("type").or_else(|| {
14310                        (current.named_child_count() == 1)
14311                            .then(|| current.named_child(0))
14312                            .flatten()
14313                    })?;
14314                    work.push(Work::Visit(child));
14315                }
14316            },
14317            Work::Wrap(wrapper) => {
14318                let root = values.pop()?;
14319                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
14320            }
14321            Work::ApplyWrappers(wrappers) => {
14322                let mut root = values.pop()?;
14323                for wrapper in wrappers.into_iter().rev() {
14324                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
14325                }
14326                values.push(root);
14327            }
14328            Work::BuildGeneric { argument_count } => {
14329                let value_count = argument_count.checked_add(1)?;
14330                let start = values.len().checked_sub(value_count)?;
14331                let mut built = values.split_off(start);
14332                let base = built.remove(0);
14333                values.push(builder.generic(base, built)?);
14334            }
14335        }
14336    }
14337    (values.len() == 1)
14338        .then(|| values.pop())
14339        .flatten()
14340        .and_then(|root| builder.finish(root))
14341}
14342
14343fn cpp_structured_named_type(
14344    node: Node<'_>,
14345    source: &str,
14346    lexical_scope: &[String],
14347) -> Option<StructuredTypeName> {
14348    let path = cpp_structured_type_path(node, source)?;
14349    let absolute = node.child_by_field_name("scope").is_none()
14350        && node.child(0).is_some_and(|child| child.kind() == "::");
14351    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
14352}
14353
14354#[derive(Clone, Copy)]
14355enum CppStructuredTypeWrapper {
14356    Pointer,
14357    LvalueReference,
14358    RvalueReference,
14359    Array,
14360}
14361
14362fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
14363    let mut wrappers = Vec::new();
14364    let mut current = node;
14365    loop {
14366        match current.kind() {
14367            "pointer_declarator" | "abstract_pointer_declarator" => {
14368                wrappers.push(CppStructuredTypeWrapper::Pointer)
14369            }
14370            "reference_declarator" | "abstract_reference_declarator" => {
14371                wrappers.push(cpp_reference_wrapper(current)?);
14372            }
14373            "array_declarator" | "abstract_array_declarator" => {
14374                wrappers.push(CppStructuredTypeWrapper::Array)
14375            }
14376            // An initializer and a grouping parenthesis are not type
14377            // structure. Stopping at one of them dropped every wrapper an
14378            // initialized or parenthesized declarator writes, so
14379            // `std::string &alias = value;` reported the unqualified
14380            // `std::string` while `const std::string &value` as a parameter
14381            // reported the reference it shares.
14382            "init_declarator" | "parenthesized_declarator" => {}
14383            _ => break,
14384        }
14385        let Some(child) = current
14386            .child_by_field_name("declarator")
14387            .or_else(|| current.named_child(0))
14388        else {
14389            break;
14390        };
14391        current = child;
14392    }
14393    Some(wrappers)
14394}
14395
14396fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
14397    node.children(&mut node.walk())
14398        .find_map(|child| match child.kind() {
14399            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
14400            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
14401            _ => None,
14402        })
14403}
14404
14405fn cpp_wrap_structured_type(
14406    identity: StructuredTypeIdentity,
14407    wrapper: CppStructuredTypeWrapper,
14408) -> Option<StructuredTypeIdentity> {
14409    match wrapper {
14410        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
14411        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
14412        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
14413        CppStructuredTypeWrapper::Array => identity.wrap_array(),
14414    }
14415}
14416
14417fn cpp_apply_structured_type_wrappers(
14418    mut identity: StructuredTypeIdentity,
14419    wrappers: Vec<CppStructuredTypeWrapper>,
14420) -> Option<StructuredTypeIdentity> {
14421    for wrapper in wrappers.into_iter().rev() {
14422        identity = cpp_wrap_structured_type(identity, wrapper)?;
14423    }
14424    Some(identity)
14425}
14426
14427fn cpp_wrap_structured_type_node(
14428    builder: &mut StructuredTypeIdentityBuilder,
14429    inner: StructuredTypeNodeId,
14430    wrapper: CppStructuredTypeWrapper,
14431) -> Option<StructuredTypeNodeId> {
14432    match wrapper {
14433        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
14434        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
14435        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
14436        CppStructuredTypeWrapper::Array => builder.array(inner),
14437    }
14438}
14439
14440/// The dot-free name components a qualified C++ name node writes, outermost
14441/// first.
14442///
14443/// The walk reads the grammar's `scope` and `name` fields rather than cutting
14444/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
14445/// shape the grammar does not name yields nothing at all.
14446pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
14447    let mut path = Vec::new();
14448    let mut stack = vec![node];
14449    while let Some(current) = stack.pop() {
14450        match current.kind() {
14451            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
14452                let component = node_text(current, source).to_string();
14453                if component.is_empty() {
14454                    return None;
14455                }
14456                path.push(component);
14457            }
14458            "template_type" | "dependent_type" => {
14459                stack.push(current.child_by_field_name("name")?);
14460            }
14461            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14462                stack.push(current.child_by_field_name("name")?);
14463                if let Some(scope) = current.child_by_field_name("scope") {
14464                    stack.push(scope);
14465                }
14466            }
14467            _ => return None,
14468        }
14469    }
14470    (!path.is_empty()).then_some(path)
14471}
14472
14473fn cpp_callable_lexical_scope<'tree>(
14474    node: Node<'tree>,
14475    source: &str,
14476    ancestry: &ParentIndex<'tree>,
14477) -> Vec<String> {
14478    let mut groups = Vec::new();
14479    let mut current = ancestry.parent(node);
14480    while let Some(parent) = current {
14481        if matches!(
14482            parent.kind(),
14483            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
14484        ) && let Some(name_node) = parent.child_by_field_name("name")
14485            && let Some(components) = cpp_structured_type_path(name_node, source)
14486            && !components.is_empty()
14487        {
14488            groups.push(components);
14489        }
14490        current = ancestry.parent(parent);
14491    }
14492    groups.reverse();
14493    groups.into_iter().flatten().collect()
14494}
14495
14496/// Whether a call on this callable's static target can select another body.
14497///
14498/// C++ says this where the member is *declared*, not where it is defined, and
14499/// that is the whole difficulty of the question: virtual-ness is inherited
14500/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
14501/// `Base::run` is), an out-of-line definition repeats none of it, and whether
14502/// a definition's qualifier names a class or a namespace is a class-table
14503/// question. So a row answers `Closed` only when its own node proves it:
14504///
14505/// * a written `final` is the one specifier that ends the family here, and it
14506///   is the only thing that closes a member of a class that has a base clause;
14507/// * a class-body member is `Closed` otherwise only when its class body writes
14508///   no base clause at all, because a base clause is the only evidence in this
14509///   file that some base may declare this member virtual;
14510/// * anything with a virtual-family boundary, a template head, or a
14511///   preprocessor or error region is `Open`;
14512/// * a qualified declaration outside every class body answers `None`: its
14513///   class body, which is where the fact is written, may be in another file.
14514///
14515/// The analyzer joins the `None` rows with the include-visible class-body rows
14516/// of the same logical callable (#3507), the same join #3509 makes for a
14517/// definition's declared access; a row that keeps `None` after that join stays
14518/// unknown and no dispatch proof may be built on it.
14519fn cpp_callable_dispatch_extensibility<'tree>(
14520    function_declarator: Node<'tree>,
14521    ancestry: &ParentIndex<'tree>,
14522) -> Option<DispatchExtensibility> {
14523    let mut declaration = None;
14524    // The first class body this declarator is written in. The upward walk
14525    // visits it anyway, so only the one step from the body to its class node
14526    // is extra, and only for a class-body member.
14527    let mut class_body = None;
14528    let mut current = Some(function_declarator);
14529    while let Some(node) = current {
14530        match node.kind() {
14531            "template_declaration"
14532            | "preproc_if"
14533            | "preproc_ifdef"
14534            | "preproc_else"
14535            | "preproc_elif"
14536            | "preproc_call"
14537            | "ERROR" => return Some(DispatchExtensibility::Open),
14538            "declaration" | "field_declaration" | "function_definition" => {
14539                declaration.get_or_insert(node);
14540            }
14541            "field_declaration_list" => {
14542                class_body.get_or_insert(node);
14543            }
14544            "translation_unit" => break,
14545            _ => {}
14546        }
14547        current = ancestry.parent(node);
14548    }
14549    let Some(declaration) = declaration else {
14550        return Some(DispatchExtensibility::Open);
14551    };
14552
14553    let mut saw_virtual_boundary = false;
14554    let mut stack = vec![declaration];
14555    while let Some(node) = stack.pop() {
14556        match node.kind() {
14557            "compound_statement" | "field_declaration_list" => continue,
14558            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
14559            "virtual"
14560            | "override"
14561            | "virtual_specifier"
14562            | "pure_virtual_clause"
14563            | "template_parameter_list"
14564            | "template_method"
14565            | "template_function"
14566            | "ERROR" => saw_virtual_boundary = true,
14567            _ => {}
14568        }
14569        let mut cursor = node.walk();
14570        stack.extend(node.children(&mut cursor));
14571    }
14572
14573    if saw_virtual_boundary {
14574        return Some(DispatchExtensibility::Open);
14575    }
14576    if let Some(body) = class_body {
14577        // C++ inherits virtual-ness, so a member written without a `virtual`
14578        // keyword is still virtual when a base declares it. Whether some base
14579        // does is not decidable here, so a base clause keeps the answer open.
14580        let has_base_clause = ancestry
14581            .parent(body)
14582            .and_then(cpp_class_like_base_clause)
14583            .is_some();
14584        return Some(if has_base_clause {
14585            DispatchExtensibility::Open
14586        } else {
14587            DispatchExtensibility::Closed
14588        });
14589    }
14590    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
14591        return None;
14592    }
14593    // A namespace or file scope callable is reached by name, never through a
14594    // receiver, so no other body can be selected for a call to it.
14595    Some(DispatchExtensibility::Closed)
14596}
14597
14598fn cpp_callable_linkage<'tree>(
14599    declaration: Node<'tree>,
14600    source: &str,
14601    ancestry: &ParentIndex<'tree>,
14602) -> CallableLinkage {
14603    let mut enclosed_by_class = false;
14604    let mut current = ancestry.parent(declaration);
14605    while let Some(node) = current {
14606        if node.kind() == "namespace_definition"
14607            && node
14608                .child_by_field_name("name")
14609                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14610        {
14611            return CallableLinkage::Internal;
14612        }
14613        if matches!(
14614            node.kind(),
14615            "class_specifier" | "struct_specifier" | "union_specifier"
14616        ) {
14617            if node
14618                .child_by_field_name("name")
14619                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
14620            {
14621                return CallableLinkage::Internal;
14622            }
14623            enclosed_by_class = true;
14624        }
14625        // An export macro between `class` and the class name can make
14626        // tree-sitter parse the entire class as a function definition. The
14627        // structured recovery proves that this container is a named class
14628        // scope, not a local callable scope.
14629        if node.kind() == "lambda_expression"
14630            || node.kind() == "function_definition"
14631                && !is_recovered_exported_class_container(node, source)
14632        {
14633            return CallableLinkage::Internal;
14634        }
14635        current = ancestry.parent(node);
14636    }
14637
14638    if enclosed_by_class {
14639        return CallableLinkage::External;
14640    }
14641
14642    if cpp_declares_static_storage_class(declaration, source) {
14643        CallableLinkage::Internal
14644    } else {
14645        CallableLinkage::External
14646    }
14647}
14648
14649fn cpp_callable_return_type_text<'tree>(
14650    function_declarator: Node<'tree>,
14651    source: &str,
14652    ancestry: &ParentIndex<'tree>,
14653) -> Option<String> {
14654    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
14655        return None;
14656    }
14657    if let Some((return_type, _)) =
14658        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
14659    {
14660        let text = normalize_cpp_whitespace(node_text(return_type, source));
14661        return (!text.is_empty()).then_some(text);
14662    }
14663    let mut cursor = function_declarator.walk();
14664    if let Some(trailing) = function_declarator
14665        .named_children(&mut cursor)
14666        .find(|child| child.kind() == "trailing_return_type")
14667        && let Some(type_descriptor) = trailing.named_child(0)
14668    {
14669        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
14670        if !text.is_empty() {
14671            return Some(text);
14672        }
14673    }
14674
14675    let mut current = function_declarator;
14676    let mut indirection = String::new();
14677    while let Some(parent) = ancestry.parent(current) {
14678        if matches!(
14679            parent.kind(),
14680            "function_definition" | "declaration" | "field_declaration"
14681        ) {
14682            let type_node = parent.child_by_field_name("type")?;
14683            if cpp_export_macro_token(node_text(type_node, source))
14684                && (0..parent.named_child_count()).any(|index| {
14685                    parent
14686                        .named_child(index)
14687                        .is_some_and(|child| child.kind() == "ERROR")
14688                })
14689            {
14690                // Export/decorator macros commonly occupy the grammar's `type`
14691                // field and leave the semantic return type in an ERROR sibling.
14692                // Do not persist the macro token as a return type. The malformed
14693                // declaration does not carry enough structured evidence here.
14694                return None;
14695            }
14696            let base = normalize_cpp_whitespace(node_text(type_node, source));
14697            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
14698        }
14699        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
14700            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
14701                && parent.named_child_count() == 1
14702                && parent.named_child(0) == Some(current));
14703        if wraps_current_declarator {
14704            match parent.kind() {
14705                "pointer_declarator" => indirection.push('*'),
14706                "reference_declarator" => {
14707                    let reference = parent
14708                        .children(&mut parent.walk())
14709                        .find(|child| !child.is_named())
14710                        .map(|child| node_text(child, source))
14711                        .unwrap_or("&");
14712                    indirection.push_str(reference);
14713                }
14714                "init_declarator" | "parenthesized_declarator" => {}
14715                _ => return None,
14716            }
14717            current = parent;
14718            continue;
14719        }
14720        return None;
14721    }
14722    None
14723}
14724
14725pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
14726    let mut required = 0;
14727    let mut total = 0;
14728    let mut repeated = false;
14729    for slot in cpp_callable_parameter_slots(parameters_node, source) {
14730        match slot {
14731            CppParameterSlot::Declared {
14732                required: is_required,
14733                ..
14734            } => {
14735                total += 1;
14736                if is_required {
14737                    required += 1;
14738                }
14739            }
14740            CppParameterSlot::OptionalVoid => total += 1,
14741            CppParameterSlot::Ellipsis => repeated = true,
14742        }
14743    }
14744    CallableArity::new(required, total, repeated)
14745}
14746
14747/// One entry of a callable's invocation parameter list, plus an optional
14748/// unnamed `void` declaration retained for the legacy arity result.
14749///
14750/// The list excludes an explicit object parameter and a lone `void`. Every
14751/// derivation of parameter arity or type starts from this same sequence, so
14752/// those views cannot disagree about which parameters exist.
14753#[derive(Clone, Copy)]
14754enum CppParameterSlot<'tree> {
14755    Declared {
14756        parameter: Node<'tree>,
14757        required: bool,
14758    },
14759    OptionalVoid,
14760    Ellipsis,
14761}
14762
14763fn cpp_callable_parameter_slots<'tree>(
14764    parameters_node: Node<'tree>,
14765    source: &str,
14766) -> Vec<CppParameterSlot<'tree>> {
14767    let mut slots = Vec::new();
14768    let mut cursor = parameters_node.walk();
14769    for parameter in parameters_node.children(&mut cursor) {
14770        match parameter.kind() {
14771            "parameter_declaration" | "optional_parameter_declaration" => {
14772                let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
14773                    && parameter
14774                        .child_by_field_name("type")
14775                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
14776                if is_unnamed_void && parameter.kind() == "parameter_declaration" {
14777                    continue;
14778                }
14779                if is_unnamed_void {
14780                    slots.push(CppParameterSlot::OptionalVoid);
14781                    continue;
14782                }
14783                slots.push(CppParameterSlot::Declared {
14784                    parameter,
14785                    required: parameter.kind() == "parameter_declaration",
14786                });
14787            }
14788            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
14789                slots.push(CppParameterSlot::Ellipsis);
14790            }
14791            _ => {}
14792        }
14793    }
14794    slots
14795}
14796
14797pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
14798    cpp_callable_parameter_slots(parameters_node, source)
14799        .into_iter()
14800        .filter_map(|slot| match slot {
14801            CppParameterSlot::Declared { parameter, .. } => {
14802                Some(cpp_parameter_type(parameter, source))
14803            }
14804            CppParameterSlot::Ellipsis => Some("...".to_string()),
14805            CppParameterSlot::OptionalVoid => None,
14806        })
14807        .collect()
14808}
14809
14810/// Return the written name of each callable parameter in invocation order.
14811/// C and C++ permit declarations without names; those slots deliberately stay
14812/// `None` so consumers can distinguish an unnamed formal from a guessed one.
14813pub(crate) fn cpp_callable_parameter_names(
14814    parameters_node: Node<'_>,
14815    source: &str,
14816) -> Vec<Option<String>> {
14817    cpp_callable_parameter_slots(parameters_node, source)
14818        .into_iter()
14819        .filter_map(|slot| match slot {
14820            CppParameterSlot::Declared { parameter, .. } => Some(
14821                parameter
14822                    .child_by_field_name("declarator")
14823                    .and_then(|declarator| declarator_name_from_node(declarator, source)),
14824            ),
14825            CppParameterSlot::Ellipsis => Some(None),
14826            CppParameterSlot::OptionalVoid => None,
14827        })
14828        .collect()
14829}
14830
14831/// One callable parameter's parser-derived type.
14832///
14833/// A rendered spelling such as `const T&` is a source text, not a type name. A
14834/// consumer that must publish a type into a structured model - a semantic-pack
14835/// type reference, for example - reads this instead.
14836#[derive(Debug, Clone, PartialEq, Eq)]
14837pub enum CppParameterType {
14838    /// The written type reduced to a structured identity. C++ cv-qualifiers
14839    /// have no place in that model and are not represented.
14840    Structured(StructuredTypeIdentity),
14841    /// A `...` pack, which declares no parameter type at all.
14842    Ellipsis,
14843    /// A written type with no structured reduction, such as a macro-obscured,
14844    /// `decltype`-computed, or function-pointer parameter.
14845    Unstructured,
14846}
14847
14848/// The structured type of each invocation parameter, in declaration order.
14849///
14850/// The result is index-parallel with the rendered
14851/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
14852/// callable.
14853pub fn cpp_callable_parameter_type_identities<'tree>(
14854    function_declarator: Node<'tree>,
14855    source: &str,
14856    ancestry: &ParentIndex<'tree>,
14857) -> Vec<CppParameterType> {
14858    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14859        return Vec::new();
14860    };
14861    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14862    cpp_callable_parameter_slots(parameters_node, source)
14863        .into_iter()
14864        .filter_map(|slot| match slot {
14865            CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
14866            CppParameterSlot::Declared { parameter, .. } => Some(
14867                cpp_parameter_type_identity(parameter, source, &lexical_scope)
14868                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
14869            ),
14870            CppParameterSlot::OptionalVoid => None,
14871        })
14872        .collect()
14873}
14874
14875/// The parser-derived type of one declared object or parameter.
14876///
14877/// The declaration owns the base `type` field while the individual declarator
14878/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
14879/// lets callers distinguish several declarators in one declaration without
14880/// reparsing a rendered signature.
14881pub fn cpp_declaration_type_identity<'tree>(
14882    declaration: Node<'tree>,
14883    declarator: Node<'tree>,
14884    source: &str,
14885    ancestry: &ParentIndex<'tree>,
14886) -> Option<StructuredTypeIdentity> {
14887    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
14888    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
14889}
14890
14891fn cpp_parameter_type_identity(
14892    parameter: Node<'_>,
14893    source: &str,
14894    lexical_scope: &[String],
14895) -> Option<StructuredTypeIdentity> {
14896    cpp_declaration_type_identity_in_scope(
14897        parameter,
14898        cpp_parameter_declarator(parameter),
14899        source,
14900        lexical_scope,
14901    )
14902}
14903
14904fn cpp_declaration_type_identity_in_scope(
14905    declaration: Node<'_>,
14906    declarator: Option<Node<'_>>,
14907    source: &str,
14908    lexical_scope: &[String],
14909) -> Option<StructuredTypeIdentity> {
14910    let type_node = declaration.child_by_field_name("type")?;
14911    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
14912    if let Some(declarator) = declarator {
14913        return cpp_apply_structured_type_wrappers(
14914            identity,
14915            cpp_structured_declarator_wrappers(declarator)?,
14916        );
14917    }
14918    Some(identity)
14919}
14920
14921/// The comparable shape of each invocation parameter, in declaration order.
14922///
14923/// The result is index-parallel with
14924/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
14925/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
14926/// must treat as evidence of nothing rather than as agreement.
14927pub fn cpp_comparable_parameter_shapes<'tree>(
14928    function_declarator: Node<'tree>,
14929    source: &str,
14930    ancestry: &ParentIndex<'tree>,
14931) -> Vec<CppComparableSlot> {
14932    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
14933        return Vec::new();
14934    };
14935    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
14936    cpp_callable_parameter_slots(parameters_node, source)
14937        .into_iter()
14938        .filter_map(|slot| match slot {
14939            CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
14940            CppParameterSlot::Declared { parameter, .. } => Some(
14941                cpp_comparable_parameter(parameter, source, &lexical_scope)
14942                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
14943            ),
14944            CppParameterSlot::OptionalVoid => None,
14945        })
14946        .collect()
14947}
14948
14949fn cpp_comparable_parameter(
14950    parameter: Node<'_>,
14951    source: &str,
14952    lexical_scope: &[String],
14953) -> Option<CppComparableParameter> {
14954    let type_node = parameter.child_by_field_name("type")?;
14955    let levels = match cpp_parameter_declarator(parameter) {
14956        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
14957        None => Vec::new(),
14958    };
14959    let mut shape = cpp_comparable_type_shape(
14960        type_node,
14961        cpp_cv_qualifiers(parameter, source),
14962        levels,
14963        source,
14964        lexical_scope,
14965    )?;
14966    shape.adjust_parameter_top_level();
14967    Some(shape)
14968}
14969
14970/// The `const` and `volatile` qualifiers written as direct named children of
14971/// `node`.
14972///
14973/// The grammar exposes `type_qualifier` as a non-field named child in exactly
14974/// the three places a parameter's qualifiers can be written: on the
14975/// `parameter_declaration` itself (the base type), on a `type_descriptor`
14976/// (inside a template argument list), and on each `pointer_declarator` level
14977/// (the pointer object). Every other qualifier the grammar admits - `restrict`
14978/// and friends - takes no part in C++ type identity, the same filter
14979/// `cpp_parameter_type` applies to the rendered spelling (#1827).
14980fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
14981    let mut qualifiers = CppCvQualifiers::default();
14982    let mut cursor = node.walk();
14983    for child in node.named_children(&mut cursor) {
14984        if child.kind() != "type_qualifier" {
14985            continue;
14986        }
14987        match node_text(child, source) {
14988            "const" => qualifiers.konst = true,
14989            "volatile" => qualifiers.volatil = true,
14990            _ => {}
14991        }
14992    }
14993    qualifiers
14994}
14995
14996#[derive(Clone, Copy, Default)]
14997struct CppCvQualifiers {
14998    konst: bool,
14999    volatil: bool,
15000}
15001
15002impl CppCvQualifiers {
15003    fn union(self, other: Self) -> Self {
15004        Self {
15005            konst: self.konst || other.konst,
15006            volatil: self.volatil || other.volatil,
15007        }
15008    }
15009}
15010
15011/// One pointer, reference or array level a declarator chain adds.
15012#[derive(Clone, Copy)]
15013enum CppComparableLevel {
15014    Pointer { konst: bool, volatil: bool },
15015    Reference,
15016    Array,
15017}
15018
15019/// The levels `declarator` adds, outermost written level first.
15020///
15021/// C++ declarator syntax binds inside out: the level written closest to the
15022/// declared name is the outermost type constructor, and tree-sitter nests it
15023/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
15024/// applies in order to reach "array of pointer to int", and the qualifier of
15025/// `int * const *p` is read on the level it was written next to, the inner
15026/// pointer of the resulting type.
15027///
15028/// A declarator chain that names a function type - a function-pointer
15029/// parameter - has no comparable shape and reports `None`, matching the
15030/// structured identity channel.
15031fn cpp_comparable_declarator_levels(
15032    declarator: Node<'_>,
15033    source: &str,
15034) -> Option<Vec<CppComparableLevel>> {
15035    let mut levels = Vec::new();
15036    let mut current = declarator;
15037    loop {
15038        match current.kind() {
15039            "pointer_declarator" | "abstract_pointer_declarator" => {
15040                let qualifiers = cpp_cv_qualifiers(current, source);
15041                levels.push(CppComparableLevel::Pointer {
15042                    konst: qualifiers.konst,
15043                    volatil: qualifiers.volatil,
15044                });
15045            }
15046            "reference_declarator" | "abstract_reference_declarator" => {
15047                levels.push(CppComparableLevel::Reference);
15048            }
15049            "array_declarator" | "abstract_array_declarator" => {
15050                levels.push(CppComparableLevel::Array);
15051            }
15052            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
15053            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
15054            _ => return None,
15055        }
15056        let Some(next) = cpp_nested_declarator(current) else {
15057            return Some(levels);
15058        };
15059        current = next;
15060    }
15061}
15062
15063/// Reduce one written type to a comparable arena.
15064///
15065/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
15066/// two additions: each visited type node carries the cv-qualifiers written on
15067/// it, and declarator levels arrive as a prepared list rather than being
15068/// rediscovered inside the walk.
15069fn cpp_comparable_type_shape(
15070    type_node: Node<'_>,
15071    qualifiers: CppCvQualifiers,
15072    levels: Vec<CppComparableLevel>,
15073    source: &str,
15074    lexical_scope: &[String],
15075) -> Option<CppComparableParameter> {
15076    enum Work<'tree> {
15077        Visit {
15078            node: Node<'tree>,
15079            qualifiers: CppCvQualifiers,
15080        },
15081        ApplyLevels(Vec<CppComparableLevel>),
15082        BuildGeneric {
15083            argument_count: usize,
15084        },
15085    }
15086
15087    let mut nodes: Vec<CppComparableNode> = Vec::new();
15088    let mut values: Vec<usize> = Vec::new();
15089    let mut work = vec![
15090        Work::ApplyLevels(levels),
15091        Work::Visit {
15092            node: type_node,
15093            qualifiers,
15094        },
15095    ];
15096    while let Some(next) = work.pop() {
15097        match next {
15098            Work::Visit { node, qualifiers } => match node.kind() {
15099                "type_descriptor" => {
15100                    let inner_type = node
15101                        .child_by_field_name("type")
15102                        .or_else(|| node.named_child(0))?;
15103                    let mut cursor = node.walk();
15104                    let declarator = node.child_by_field_name("declarator").or_else(|| {
15105                        node.named_children(&mut cursor).find(|child| {
15106                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
15107                        })
15108                    });
15109                    let levels = match declarator {
15110                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
15111                        None => Vec::new(),
15112                    };
15113                    work.push(Work::ApplyLevels(levels));
15114                    work.push(Work::Visit {
15115                        node: inner_type,
15116                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
15117                    });
15118                }
15119                "sized_type_specifier" => {
15120                    // `unsigned char` is one primitive type whose components are
15121                    // partly unnamed tokens, so the whole specifier is its own
15122                    // name component. Reducing it to the `type` child would make
15123                    // `f(unsigned char)` and `f(char)` compare equal.
15124                    let name = StructuredTypeName::new(
15125                        vec![normalize_cpp_whitespace(node_text(node, source))],
15126                        lexical_scope.to_vec(),
15127                        false,
15128                    )?;
15129                    values.push(cpp_push_comparable_node(
15130                        &mut nodes,
15131                        CppComparableNode::Named {
15132                            name,
15133                            primitive: true,
15134                            konst: qualifiers.konst,
15135                            volatil: qualifiers.volatil,
15136                        },
15137                    ));
15138                }
15139                "qualified_identifier"
15140                | "scoped_identifier"
15141                | "scoped_type_identifier"
15142                | "type_identifier"
15143                | "field_identifier"
15144                | "identifier"
15145                | "namespace_identifier"
15146                | "primitive_type"
15147                | "template_type" => {
15148                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
15149                    values.push(cpp_push_comparable_node(
15150                        &mut nodes,
15151                        CppComparableNode::Named {
15152                            name,
15153                            primitive: node.kind() == "primitive_type",
15154                            konst: qualifiers.konst,
15155                            volatil: qualifiers.volatil,
15156                        },
15157                    ));
15158                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
15159                        let mut cursor = arguments_node.walk();
15160                        let arguments = arguments_node
15161                            .named_children(&mut cursor)
15162                            .filter(|child| !child.is_extra() && child.kind() != "comment")
15163                            .collect::<Vec<_>>();
15164                        work.push(Work::BuildGeneric {
15165                            argument_count: arguments.len(),
15166                        });
15167                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
15168                            node: argument,
15169                            qualifiers: CppCvQualifiers::default(),
15170                        }));
15171                    }
15172                }
15173                _ => {
15174                    let inner = node.child_by_field_name("type").or_else(|| {
15175                        (node.named_child_count() == 1)
15176                            .then(|| node.named_child(0))
15177                            .flatten()
15178                    })?;
15179                    work.push(Work::Visit {
15180                        node: inner,
15181                        qualifiers,
15182                    });
15183                }
15184            },
15185            Work::ApplyLevels(levels) => {
15186                let mut root = values.pop()?;
15187                for level in levels {
15188                    let node = match level {
15189                        CppComparableLevel::Pointer { konst, volatil } => {
15190                            CppComparableNode::Pointer {
15191                                inner: root,
15192                                konst,
15193                                volatil,
15194                            }
15195                        }
15196                        CppComparableLevel::Reference => {
15197                            CppComparableNode::Reference { inner: root }
15198                        }
15199                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
15200                    };
15201                    root = cpp_push_comparable_node(&mut nodes, node);
15202                }
15203                values.push(root);
15204            }
15205            Work::BuildGeneric { argument_count } => {
15206                let value_count = argument_count.checked_add(1)?;
15207                let start = values.len().checked_sub(value_count)?;
15208                let mut built = values.split_off(start);
15209                let base = built.remove(0);
15210                values.push(cpp_push_comparable_node(
15211                    &mut nodes,
15212                    CppComparableNode::Generic {
15213                        base,
15214                        arguments: built,
15215                    },
15216                ));
15217            }
15218        }
15219    }
15220    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
15221    debug_assert_eq!(
15222        root,
15223        nodes.len().saturating_sub(1),
15224        "comparable nodes are appended in post-order, so the root is the last one"
15225    );
15226    Some(CppComparableParameter::new(nodes, root))
15227}
15228
15229fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
15230    nodes.push(node);
15231    nodes.len() - 1
15232}
15233
15234/// The template argument list of the name `node` terminates in, if any.
15235///
15236/// `std::vector<int>` writes its arguments on the `name` of a qualified
15237/// identifier, so a walk that stopped at the qualified node would reduce
15238/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
15239fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
15240    let mut current = node;
15241    loop {
15242        match current.kind() {
15243            "template_type" => return current.child_by_field_name("arguments"),
15244            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15245                current = current.child_by_field_name("name")?;
15246            }
15247            _ => return None,
15248        }
15249    }
15250}
15251
15252/// The callable declarator of the declaration that covers `start_byte`.
15253///
15254/// A consumer that holds a declaration's recorded byte position rather than its
15255/// syntax node - external header extraction, for instance - uses this to reach
15256/// the same `function_declarator` the declaration walk read.
15257pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
15258    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
15259    loop {
15260        if matches!(
15261            current.kind(),
15262            "declaration" | "field_declaration" | "function_definition"
15263        ) && let Some(declarator) = current
15264            .child_by_field_name("declarator")
15265            .and_then(extract_function_declarator)
15266        {
15267            return Some(declarator);
15268        }
15269        current = current.parent()?;
15270    }
15271}
15272
15273fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
15274    let mut labels = Vec::new();
15275    let mut cursor = parameters_node.walk();
15276    for child in parameters_node.children(&mut cursor) {
15277        match child.kind() {
15278            "parameter_declaration" | "optional_parameter_declaration" => {
15279                if let Some(name_node) = child
15280                    .child_by_field_name("declarator")
15281                    .and_then(cpp_declarator_label_node)
15282                {
15283                    labels.push(name_node);
15284                } else {
15285                    labels.push(child);
15286                }
15287            }
15288            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
15289                labels.push(child);
15290            }
15291            _ => {}
15292        }
15293    }
15294    labels
15295}
15296
15297fn cpp_signature_search_start<'tree>(
15298    signature: &str,
15299    function_declarator: Node<'tree>,
15300    source: &str,
15301    ancestry: &ParentIndex<'tree>,
15302) -> usize {
15303    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
15304        return 0;
15305    };
15306    let raw = node_text(enclosing, source);
15307    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
15308    let offset = function_declarator
15309        .start_byte()
15310        .saturating_sub(enclosing.start_byte())
15311        .saturating_sub(leading_trim_bytes);
15312    offset.min(signature.len())
15313}
15314
15315fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
15316    match node.kind() {
15317        "identifier" | "field_identifier" => Some(node),
15318        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
15319            .child_by_field_name("declarator")
15320            .or_else(|| last_named_child(node))
15321            .and_then(cpp_declarator_label_node),
15322        "array_declarator" => node
15323            .child_by_field_name("declarator")
15324            .and_then(cpp_declarator_label_node),
15325        "function_declarator" => node
15326            .child_by_field_name("declarator")
15327            .or_else(|| node.child_by_field_name("name"))
15328            .or_else(|| last_named_child(node))
15329            .and_then(cpp_declarator_label_node),
15330        _ => None,
15331    }
15332}
15333
15334fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
15335    let base_type = parameter
15336        .child_by_field_name("type")
15337        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
15338        .unwrap_or_default();
15339    let declarator = cpp_parameter_declarator(parameter);
15340    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
15341    // are discarded, so `f(const int)` and `f(int)` declare one function. A
15342    // qualifier written next to the parameter's type is only top-level when
15343    // the declarator adds no indirection; behind a pointer, reference or array
15344    // declarator the same qualifier belongs to the pointee, referent or
15345    // element and keeps distinguishing the type (#1827).
15346    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
15347    let mut cursor = parameter.walk();
15348    let qualifiers = parameter
15349        .named_children(&mut cursor)
15350        .filter(|child| child.kind() == "type_qualifier")
15351        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
15352        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
15353        .collect::<Vec<_>>()
15354        .join(" ");
15355    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
15356        (true, _) => base_type,
15357        (_, true) => qualifiers,
15358        (false, false) => format!("{qualifiers} {base_type}"),
15359    };
15360    let declarator_suffix = declarator
15361        .map(|node| cpp_declarator_suffix_without_name(node, source))
15362        .unwrap_or_default();
15363
15364    let combined = if type_text.is_empty() {
15365        declarator_suffix
15366    } else if declarator_suffix.is_empty() {
15367        type_text
15368    } else {
15369        format!("{type_text} {declarator_suffix}")
15370    };
15371    normalize_cpp_type_text(&combined)
15372}
15373
15374fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
15375    parameter.child_by_field_name("declarator").or_else(|| {
15376        // Some unnamed prototype parameters expose their abstract declarator
15377        // as a direct named child without the grammar's `declarator` field.
15378        // Recover only the structured abstract-declarator node; the parameter's
15379        // type and qualifiers are distinct children and must not be guessed from
15380        // source text.
15381        cpp_first_abstract_declarator_child(parameter)
15382    })
15383}
15384
15385/// Whether a parameter's declarator chain adds indirection - a pointer,
15386/// reference, array or function declarator - to the parameter's written type.
15387pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
15388    let mut current = Some(declarator);
15389    while let Some(node) = current {
15390        if matches!(
15391            node.kind(),
15392            "pointer_declarator"
15393                | "abstract_pointer_declarator"
15394                | "reference_declarator"
15395                | "abstract_reference_declarator"
15396                | "array_declarator"
15397                | "abstract_array_declarator"
15398                | "function_declarator"
15399                | "abstract_function_declarator"
15400        ) {
15401            return true;
15402        }
15403        current = cpp_nested_declarator(node);
15404    }
15405    false
15406}
15407
15408fn is_cpp_abstract_declarator(kind: &str) -> bool {
15409    matches!(
15410        kind,
15411        "abstract_pointer_declarator"
15412            | "abstract_reference_declarator"
15413            | "abstract_array_declarator"
15414            | "abstract_function_declarator"
15415            | "abstract_parenthesized_declarator"
15416    )
15417}
15418
15419fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
15420    let mut cursor = node.walk();
15421    node.named_children(&mut cursor)
15422        .find(|child| is_cpp_abstract_declarator(child.kind()))
15423}
15424
15425fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
15426    node.child_by_field_name("declarator").or_else(|| {
15427        if is_cpp_abstract_declarator(node.kind()) {
15428            cpp_first_abstract_declarator_child(node)
15429        } else {
15430            // Named declarators historically use their last named child when
15431            // tree-sitter omits the field. Keep that broad fallback for
15432            // attributed, variadic, and recovered named shapes.
15433            last_named_child(node)
15434        }
15435    })
15436}
15437
15438fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
15439    match node.kind() {
15440        "identifier" | "field_identifier" => String::new(),
15441        "pointer_declarator" | "abstract_pointer_declarator" => {
15442            let inner = cpp_nested_declarator(node)
15443                .map(|child| cpp_declarator_suffix_without_name(child, source))
15444                .unwrap_or_default();
15445            format!("*{inner}")
15446        }
15447        "reference_declarator" | "abstract_reference_declarator" => {
15448            let inner = cpp_nested_declarator(node)
15449                .map(|child| cpp_declarator_suffix_without_name(child, source))
15450                .unwrap_or_default();
15451            let reference = node
15452                .children(&mut node.walk())
15453                .find(|child| matches!(child.kind(), "&" | "&&"))
15454                .map(|child| node_text(child, source))
15455                .unwrap_or("&");
15456            format!("{reference}{inner}")
15457        }
15458        "array_declarator" | "abstract_array_declarator" => {
15459            let inner = cpp_nested_declarator(node)
15460                .map(|child| cpp_declarator_suffix_without_name(child, source))
15461                .unwrap_or_default();
15462            let size = node
15463                .child_by_field_name("size")
15464                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
15465                .unwrap_or_default();
15466            format!("{inner}[{size}]")
15467        }
15468        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
15469            let inner = cpp_nested_declarator(node);
15470            inner
15471                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
15472                .unwrap_or_default()
15473        }
15474        "function_declarator" | "abstract_function_declarator" => {
15475            let inner = cpp_nested_declarator(node)
15476                .map(|child| cpp_declarator_suffix_without_name(child, source))
15477                .unwrap_or_default();
15478            let params = node
15479                .child_by_field_name("parameters")
15480                .map(|child| cpp_parameter_signature(child, source))
15481                .unwrap_or_else(|| "()".to_string());
15482            format!("{inner}{params}")
15483        }
15484        _ => {
15485            let text = normalize_cpp_whitespace(node_text(node, source));
15486            let name = extract_declarator_name(node, source);
15487            if name.is_empty() {
15488                text
15489            } else {
15490                text.replace(&name, "").trim().to_string()
15491            }
15492        }
15493    }
15494}
15495
15496fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
15497    collapse_cpp_whitespace(
15498        suffix
15499            .trim()
15500            .trim_start_matches("->")
15501            .trim_start_matches('{')
15502            .trim_end_matches(';'),
15503    )
15504}
15505
15506pub fn normalize_cpp_whitespace(value: &str) -> String {
15507    collapse_cpp_whitespace(value)
15508}
15509
15510fn normalize_cpp_type_text(value: &str) -> String {
15511    collapse_cpp_whitespace(value)
15512        .replace(", ", ",")
15513        .replace(" <", "<")
15514        .replace("< ", "<")
15515        .replace(" >", ">")
15516}
15517
15518fn collapse_cpp_whitespace(value: &str) -> String {
15519    let mut result = String::new();
15520    let mut prev_space = false;
15521    for ch in value.chars() {
15522        if ch.is_whitespace() {
15523            if !prev_space {
15524                result.push(' ');
15525            }
15526            prev_space = true;
15527        } else {
15528            result.push(ch);
15529            prev_space = false;
15530        }
15531    }
15532    result.trim().to_string()
15533}
15534
15535fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
15536    child_by_field_or_named_child_of_kinds(
15537        node,
15538        "body",
15539        &[
15540            "declaration_list",
15541            "field_declaration_list",
15542            "enumerator_list",
15543        ],
15544    )
15545}
15546
15547/// Return a class body's actual closing brace when the parser supplied one.
15548///
15549/// A malformed namespace sentinel can leave a class node carrying unrelated
15550/// parser errors even though its own class body is complete.  `has_error()` is
15551/// therefore too coarse an admission predicate for sentinel ownership.  The
15552/// body list, however, exposes the opening and closing punctuation directly;
15553/// a real (non-missing) final `}` proves that the class did not borrow the
15554/// enclosing namespace's close.  Requiring the body to end before its parent
15555/// container also rejects a recovered node whose body swallowed that outer
15556/// boundary.
15557fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
15558    if !matches!(
15559        node.kind(),
15560        "class_specifier" | "struct_specifier" | "union_specifier"
15561    ) {
15562        return None;
15563    }
15564    let body = cpp_body_node(node)?;
15565    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
15566        return None;
15567    }
15568    let open = body.child(0)?;
15569    let close = body.child(body.child_count().checked_sub(1)?)?;
15570    if open.kind() != "{"
15571        || open.is_missing()
15572        || close.kind() != "}"
15573        || close.is_missing()
15574        || close.end_byte() != body.end_byte()
15575        || body.end_byte() > node.end_byte()
15576        || node
15577            .parent()
15578            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
15579    {
15580        return None;
15581    }
15582    Some(close)
15583}
15584
15585fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
15586    if node.kind() == "namespace_definition" {
15587        return true;
15588    }
15589    let mut cursor = node.walk();
15590    node.named_children(&mut cursor)
15591        .any(cpp_contains_namespace_definition)
15592}
15593
15594struct CppNestedNamespaceSentinel<'tree> {
15595    function: Node<'tree>,
15596    body: Node<'tree>,
15597    namespace_components: Vec<String>,
15598}
15599
15600/// Owned structural recovery metadata for a namespace-sentinel region.
15601///
15602/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
15603/// instead of the namespace/class scopes that the declaration visitor restores.
15604/// The inverted usage walk has the original CST, so it needs the same ownership
15605/// evidence without borrowing parser nodes across its file scan.  Keep this
15606/// descriptor deliberately source-range based: callers can match a reference
15607/// node by containment and then resolve its structured type spelling in the
15608/// recovered class scope.
15609#[derive(Debug, Clone)]
15610pub struct CppSentinelRecoveredOwner {
15611    pub range: Range,
15612    /// Start of the qualified owner name (`btree<P>::method`).  A leading
15613    /// return type before this byte is looked up from the namespace; parameters,
15614    /// trailing returns, and the body use the member owner scope.
15615    pub owner_name_start_byte: usize,
15616    /// Number of leading components belonging to the namespace rather than
15617    /// the qualified class owner.  A leading return type is looked up before
15618    /// every owner component, not merely before the innermost class.
15619    pub namespace_component_count: usize,
15620    pub scope_components: Vec<String>,
15621}
15622
15623#[derive(Debug, Clone)]
15624pub struct CppSentinelRecoveredClass {
15625    pub namespace_range: Range,
15626    pub namespace_scope_components: Vec<String>,
15627    pub class_range: Range,
15628    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
15629    pub scope_components: Vec<String>,
15630    /// Qualified out-of-line member definitions owned by this class.  Their
15631    /// ranges may extend beyond `class_range` when the malformed sentinel
15632    /// swallowed the namespace close and left definitions as function siblings.
15633    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
15634}
15635
15636/// Resolve the lexical scope restored for a node in a malformed
15637/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
15638/// outrank class spans, which in turn outrank the surviving namespace body.
15639/// The class ancestor suffix is recovered from the original CST so nested
15640/// members keep their complete `Outer::Inner` owner chain.
15641pub fn cpp_sentinel_recovered_scope_for_node(
15642    node: Node<'_>,
15643    source: &str,
15644    recovered_classes: &[CppSentinelRecoveredClass],
15645) -> Option<Vec<String>> {
15646    let contains =
15647        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
15648    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
15649    for recovered in recovered_classes {
15650        for owner in recovered
15651            .owner_ranges
15652            .iter()
15653            .filter(|owner| contains(owner.range))
15654        {
15655            let replace = best_owner.is_none_or(|existing| {
15656                owner.range.end_byte.saturating_sub(owner.range.start_byte)
15657                    < existing
15658                        .range
15659                        .end_byte
15660                        .saturating_sub(existing.range.start_byte)
15661            });
15662            if replace {
15663                best_owner = Some(owner);
15664            }
15665        }
15666    }
15667    if let Some(owner) = best_owner {
15668        let mut scope = owner.scope_components.clone();
15669        if node.start_byte() < owner.owner_name_start_byte {
15670            scope.truncate(owner.namespace_component_count);
15671        }
15672        return Some(scope);
15673    }
15674
15675    let class = recovered_classes
15676        .iter()
15677        .filter(|recovered| contains(recovered.class_range))
15678        .min_by_key(|recovered| {
15679            recovered
15680                .class_range
15681                .end_byte
15682                .saturating_sub(recovered.class_range.start_byte)
15683        });
15684    let class_scope = class.is_some();
15685    let mut scope = if let Some(class) = class {
15686        class.scope_components.clone()
15687    } else {
15688        let namespace = recovered_classes
15689            .iter()
15690            .filter(|recovered| contains(recovered.namespace_range))
15691            .min_by_key(|recovered| {
15692                recovered
15693                    .namespace_range
15694                    .end_byte
15695                    .saturating_sub(recovered.namespace_range.start_byte)
15696            })?;
15697        let mut scope = namespace.namespace_scope_components.clone();
15698        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
15699        let common_prefix = scope
15700            .iter()
15701            .zip(&parser_namespace)
15702            .take_while(|(recovered, parser)| recovered == parser)
15703            .count();
15704        scope.extend(parser_namespace.into_iter().skip(common_prefix));
15705        scope
15706    };
15707    if class_scope {
15708        let mut ancestor_components = Vec::new();
15709        let mut ancestor = node.parent();
15710        while let Some(current) = ancestor {
15711            if matches!(
15712                current.kind(),
15713                "class_specifier" | "struct_specifier" | "union_specifier"
15714            ) && let Some(name) = current.child_by_field_name("name")
15715                && let Some(name_components) = cpp_name_components(name, source)
15716            {
15717                ancestor_components.push(
15718                    name_components
15719                        .into_iter()
15720                        .map(|component| component.name)
15721                        .collect::<Vec<_>>(),
15722                );
15723            }
15724            ancestor = current.parent();
15725        }
15726        ancestor_components.reverse();
15727        let base_len = scope.len();
15728        for component in ancestor_components.into_iter().flatten() {
15729            if scope.len() >= base_len && scope.last() == Some(&component) {
15730                continue;
15731            }
15732            scope.push(component);
15733        }
15734    }
15735    Some(scope)
15736}
15737
15738struct CppSentinelFragmentedClassTail<'tree> {
15739    class_node: Node<'tree>,
15740    template_node: Option<Node<'tree>>,
15741    name: String,
15742    raw_supertypes: Option<Vec<String>>,
15743    fragmented: FragmentedExportBody,
15744    consumed_start: usize,
15745}
15746
15747struct CppSentinelFragmentedClassErrorPrefix<'tree> {
15748    name: String,
15749    open: Node<'tree>,
15750    raw_supertypes: Option<Vec<String>>,
15751}
15752
15753struct CppSentinelDirectBodyClassRegion {
15754    namespace_components: Vec<String>,
15755    class_start: usize,
15756    class_start_line: usize,
15757    class_close_end: usize,
15758    class_close_line: usize,
15759    name: String,
15760}
15761
15762fn cpp_sentinel_body_class_candidate<'tree>(
15763    child: Node<'tree>,
15764) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15765    if matches!(
15766        child.kind(),
15767        "class_specifier" | "struct_specifier" | "union_specifier"
15768    ) {
15769        return Some((child, None));
15770    }
15771    if child.kind() != "template_declaration" {
15772        if child.kind() == "declaration" {
15773            return Some((first_class_like_child(child)?, None));
15774        }
15775        return None;
15776    }
15777    let mut cursor = child.walk();
15778    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
15779        if matches!(
15780            candidate.kind(),
15781            "class_specifier" | "struct_specifier" | "union_specifier"
15782        ) {
15783            Some(candidate)
15784        } else if candidate.kind() == "declaration" {
15785            first_class_like_child(candidate)
15786        } else {
15787            None
15788        }
15789    })?;
15790    Some((class_node, Some(child)))
15791}
15792
15793/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
15794/// namespace-sentinel body when a later member macro ends the bogus sentinel
15795/// function before the real class close. The anonymous class/open tokens and
15796/// direct identifier are the structural proof; a retained direct close would
15797/// be an ordinary malformed class rather than the fragmented tail handled here.
15798fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
15799    node: Node<'tree>,
15800    source: &str,
15801) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
15802    let name = malformed_class_error_owner_name(node, source)?;
15803    let mut cursor = node.walk();
15804    let children = node.children(&mut cursor).collect::<Vec<_>>();
15805    let keyword = children.first()?;
15806    let open_index = children.iter().position(|child| child.kind() == "{")?;
15807    if children[open_index + 1..]
15808        .iter()
15809        .any(|child| child.kind() == "}")
15810    {
15811        return None;
15812    }
15813    let raw_supertypes =
15814        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
15815    Some(CppSentinelFragmentedClassErrorPrefix {
15816        name,
15817        open: children[open_index],
15818        raw_supertypes,
15819    })
15820}
15821
15822fn cpp_sentinel_direct_body_class_candidate<'tree>(
15823    child: Node<'tree>,
15824) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
15825    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
15826        return Some(candidate);
15827    }
15828    if child.kind() != "template_declaration" {
15829        return None;
15830    }
15831    let mut cursor = child.walk();
15832    let wrapper = child
15833        .named_children(&mut cursor)
15834        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
15835    Some((first_class_like_child(wrapper)?, Some(child)))
15836}
15837
15838fn cpp_sentinel_direct_namespace_components(
15839    function: Node<'_>,
15840    body: Node<'_>,
15841    source: &str,
15842) -> Option<Vec<String>> {
15843    let mut cursor = function.walk();
15844    let children = function
15845        .named_children(&mut cursor)
15846        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
15847        .collect::<Vec<_>>();
15848    let sentinel_index = children.iter().rposition(|child| {
15849        direct_identifier_name(*child, source)
15850            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
15851    })?;
15852    let mut identifiers = Vec::new();
15853    let mut stack = children[sentinel_index + 1..]
15854        .iter()
15855        .rev()
15856        .copied()
15857        .collect::<Vec<_>>();
15858    while let Some(current) = stack.pop() {
15859        if let Some(name) = direct_identifier_name(current, source) {
15860            identifiers.push(name);
15861            continue;
15862        }
15863        let mut cursor = current.walk();
15864        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15865        stack.extend(children.into_iter().rev());
15866    }
15867    let [keyword, namespace] = identifiers.as_slice() else {
15868        return None;
15869    };
15870    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
15871        .then(|| vec![namespace.clone()])
15872}
15873
15874fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
15875    let mut sibling = class_semicolon.next_named_sibling();
15876    let namespace_close = loop {
15877        let Some(current) = sibling else {
15878            return false;
15879        };
15880        sibling = current.next_named_sibling();
15881        if current.kind() != "comment" {
15882            break current;
15883        }
15884    };
15885    if !cpp_is_stray_close_brace(namespace_close, source) {
15886        return false;
15887    }
15888    loop {
15889        let Some(current) = sibling else {
15890            return false;
15891        };
15892        sibling = current.next_named_sibling();
15893        if current.kind() == "comment" {
15894            continue;
15895        }
15896        return direct_identifier_name(current, source)
15897            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
15898    }
15899}
15900
15901fn cpp_sentinel_macro_body_class_region<'tree>(
15902    node: Node<'tree>,
15903    source: &str,
15904    ancestry: &ParentIndex<'tree>,
15905) -> Option<CppSentinelDirectBodyClassRegion> {
15906    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
15907        return None;
15908    };
15909    if node.kind() != "function_definition" || !node.has_error() {
15910        return None;
15911    }
15912    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
15913    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
15914    let mut cursor = body.walk();
15915    let candidates = body
15916        .named_children(&mut cursor)
15917        .filter_map(cpp_sentinel_direct_body_class_candidate)
15918        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
15919        .collect::<Vec<_>>();
15920    let [(class_node, template_node)] = candidates.as_slice() else {
15921        return None;
15922    };
15923    let original_body = cpp_body_node(*class_node)?;
15924    let name = class_like_name(*class_node, source, ancestry)?;
15925    if name.is_empty() || cpp_export_macro_token(&name) {
15926        return None;
15927    }
15928
15929    let mut sibling = node.next_named_sibling();
15930    let (class_close_start, class_close_end, class_close_line) = loop {
15931        let current = sibling?;
15932        let next = current.next_named_sibling();
15933        if cpp_is_stray_close_brace(current, source)
15934            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15935        {
15936            let semicolon = next.expect("checked above");
15937            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
15938                return None;
15939            }
15940            break (
15941                current.start_byte(),
15942                semicolon.end_byte(),
15943                semicolon.end_position().row + 1,
15944            );
15945        }
15946        sibling = next;
15947    };
15948    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
15949    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15950    let root = tree.root_node();
15951    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
15952    // The region reparse is its own tree, so it needs its own parent index;
15953    // the caller's index answers nothing about these nodes.
15954    let reparsed_ancestry = ParentIndex::new(root);
15955    let reparsed =
15956        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
15957    if reparsed.name != name
15958        || reparsed.declaration_node.start_byte() != class_node.start_byte()
15959        || reparsed.body.start_byte() != original_body.start_byte()
15960        || class_close_start <= reparsed.body.end_byte()
15961        || class_close_end <= class_node.end_byte()
15962    {
15963        return None;
15964    }
15965    Some(CppSentinelDirectBodyClassRegion {
15966        namespace_components,
15967        class_start: reparse_start,
15968        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
15969            node.start_position().row + 1
15970        }),
15971        class_close_end,
15972        class_close_line,
15973        name,
15974    })
15975}
15976
15977/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
15978/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
15979///
15980/// The parser puts the namespace opener and the malformed function in one root
15981/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
15982/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
15983/// the malformed function must begin with an all-caps type, then an ERROR whose
15984/// sole identifier is `namespace`, followed by the inner namespace identifier
15985/// and a compound body; and that body must contain a complete named class or a
15986/// structurally fragmented class prefix. A text reparse cannot prove any of
15987/// those ownership boundaries.
15988fn cpp_nested_namespace_sentinel<'tree>(
15989    node: Node<'tree>,
15990    source: &str,
15991    ancestry: &ParentIndex<'tree>,
15992) -> Option<CppNestedNamespaceSentinel<'tree>> {
15993    if !node.has_error() {
15994        return None;
15995    }
15996
15997    let (function, mut namespace_components) = if node.kind() == "ERROR" {
15998        let mut cursor = node.walk();
15999        let functions = node
16000            .named_children(&mut cursor)
16001            .filter(|child| child.kind() == "function_definition")
16002            .collect::<Vec<_>>();
16003        let [function] = functions.as_slice() else {
16004            return None;
16005        };
16006        if !function.has_error() {
16007            return None;
16008        }
16009        let mut cursor = node.walk();
16010        let children = node.children(&mut cursor).collect::<Vec<_>>();
16011        let function_index = children
16012            .iter()
16013            .position(|child| same_node(*child, *function))?;
16014        let [outer_keyword, outer_name, outer_open] =
16015            children.get(function_index.checked_sub(3)?..function_index)?
16016        else {
16017            return None;
16018        };
16019        if outer_keyword.kind() != "namespace"
16020            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
16021            || outer_open.kind() != "{"
16022        {
16023            return None;
16024        }
16025        (
16026            *function,
16027            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
16028        )
16029    } else if node.kind() == "function_definition" {
16030        let declaration_list = node.parent()?;
16031        let namespace = declaration_list.parent()?;
16032        if declaration_list.kind() != "declaration_list"
16033            || namespace.kind() != "namespace_definition"
16034            || namespace.child_by_field_name("body") != Some(declaration_list)
16035        {
16036            return None;
16037        }
16038        (node, Vec::new())
16039    } else {
16040        return None;
16041    };
16042
16043    let mut cursor = function.walk();
16044    let named = function
16045        .named_children(&mut cursor)
16046        .filter(|child| child.kind() != "comment")
16047        .collect::<Vec<_>>();
16048    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
16049        return None;
16050    };
16051    if first_type.kind() != "type_identifier" {
16052        return None;
16053    }
16054    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
16055    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16056        return None;
16057    }
16058    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
16059        return None;
16060    }
16061    let inner_keyword = inner_error.named_child(0)?;
16062    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
16063        return None;
16064    }
16065    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
16066        return None;
16067    }
16068    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
16069    if inner_name.is_empty() || body.kind() != "compound_statement" {
16070        return None;
16071    }
16072    namespace_components.push(inner_name);
16073
16074    let mut cursor = body.walk();
16075    let has_complete_class = body.named_children(&mut cursor).any(|child| {
16076        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
16077            cpp_body_node(class_node).is_some()
16078                && class_like_name(class_node, source, ancestry)
16079                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
16080        })
16081    });
16082    if !has_complete_class
16083        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
16084    {
16085        return None;
16086    }
16087
16088    Some(CppNestedNamespaceSentinel {
16089        function,
16090        body: *body,
16091        namespace_components,
16092    })
16093}
16094
16095/// Recognize a namespace-begin sentinel directly beneath the translation unit.
16096///
16097/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
16098/// function whose type is the sentinel, whose declarator is the structured
16099/// qualified name `namespace::a::b`, and whose body contains the namespace
16100/// items. Declaration indexing already reparses this bounded region. The
16101/// inverse scanner retains the original tree, so recover the same namespace
16102/// components from the declarator fields for its lexical-scope metadata.
16103fn cpp_root_namespace_sentinel<'tree>(
16104    node: Node<'tree>,
16105    source: &str,
16106    ancestry: &ParentIndex<'tree>,
16107) -> Option<CppNestedNamespaceSentinel<'tree>> {
16108    if node.kind() != "function_definition"
16109        || !node.has_error()
16110        || node.parent()?.kind() != "translation_unit"
16111    {
16112        return None;
16113    }
16114    let first_type = node.child_by_field_name("type")?;
16115    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
16116    if first_type.kind() != "type_identifier"
16117        || sentinel.is_empty()
16118        || !cpp_export_macro_token(&sentinel)
16119    {
16120        return None;
16121    }
16122    let declarator = node.child_by_field_name("declarator")?;
16123    let body = node.child_by_field_name("body")?;
16124    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
16125        return None;
16126    }
16127    let mut cursor = node.walk();
16128    let named = node
16129        .named_children(&mut cursor)
16130        .filter(|child| child.kind() != "comment")
16131        .collect::<Vec<_>>();
16132    let [named_type, named_declarator, named_body] = named.as_slice() else {
16133        return None;
16134    };
16135    if !same_node(*named_type, first_type)
16136        || !same_node(*named_declarator, declarator)
16137        || !same_node(*named_body, body)
16138    {
16139        return None;
16140    }
16141    let mut declarator_components = Vec::new();
16142    let mut valid_components = true;
16143    walk_named_tree_preorder(declarator, true, |component| {
16144        if !matches!(
16145            component.kind(),
16146            "identifier" | "namespace_identifier" | "type_identifier"
16147        ) {
16148            return WalkControl::Continue;
16149        }
16150        let Some(component) = canonical_cpp_qualified_component(component, source) else {
16151            valid_components = false;
16152            return WalkControl::Break;
16153        };
16154        declarator_components.push(component.name);
16155        WalkControl::SkipChildren
16156    });
16157    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
16158        return None;
16159    }
16160    declarator_components.remove(0);
16161    let namespace_components = declarator_components;
16162    if namespace_components.is_empty()
16163        || namespace_components
16164            .iter()
16165            .any(|component| component.is_empty() || cpp_export_macro_token(component))
16166    {
16167        return None;
16168    }
16169
16170    let mut cursor = body.walk();
16171    let has_complete_class = body.named_children(&mut cursor).any(|child| {
16172        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
16173            cpp_body_node(class_node).is_some()
16174                && class_like_name(class_node, source, ancestry)
16175                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
16176        })
16177    });
16178    if !has_complete_class
16179        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
16180    {
16181        return None;
16182    }
16183
16184    Some(CppNestedNamespaceSentinel {
16185        function: node,
16186        body,
16187        namespace_components,
16188    })
16189}
16190
16191/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
16192/// malformed namespace-sentinel function.  The recovery is deliberately
16193/// structural: the class must be a direct body item, its own class node must be
16194/// erroneous and end before a unique anonymous `}` in the enclosing
16195/// declaration-list, and that namespace's next sibling must be a standalone
16196/// `;`.  The complete interior must pass the existing member-shaped reparse
16197/// gate. This avoids source brace scans and does not borrow a close from an
16198/// unrelated later declaration.
16199fn cpp_sentinel_fragmented_class_tail<'tree>(
16200    function: Node<'tree>,
16201    body: Node<'tree>,
16202    source: &str,
16203    ancestry: &ParentIndex<'tree>,
16204) -> Option<CppSentinelFragmentedClassTail<'tree>> {
16205    let mut cursor = body.walk();
16206    let candidates = body
16207        .named_children(&mut cursor)
16208        .filter_map(|child| {
16209            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
16210                let class_body = cpp_body_node(class_node)?;
16211                if !class_node.has_error() {
16212                    return None;
16213                }
16214                let name = class_like_name(class_node, source, ancestry)?;
16215                let raw_supertypes =
16216                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
16217                        .then(|| extract_cpp_supertypes(class_node, source));
16218                return Some((
16219                    class_node,
16220                    template_node,
16221                    name,
16222                    class_body,
16223                    class_body.start_byte().checked_add(1)?,
16224                    raw_supertypes,
16225                ));
16226            }
16227            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
16228            Some((
16229                child,
16230                None,
16231                prefix.name,
16232                prefix.open,
16233                prefix.open.end_byte(),
16234                prefix.raw_supertypes,
16235            ))
16236        })
16237        .collect::<Vec<_>>();
16238    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
16239        candidates.as_slice()
16240    else {
16241        return None;
16242    };
16243    if name.is_empty() || cpp_export_macro_token(name) {
16244        return None;
16245    }
16246
16247    let (close, semicolon) =
16248        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
16249
16250    let reparse_end = close.start_byte();
16251    if *reparse_start >= reparse_end {
16252        return None;
16253    }
16254    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
16255    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
16256        return None;
16257    }
16258    let class_range = Range {
16259        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
16260        end_byte: semicolon.end_byte(),
16261        start_line: template_node.map_or(class_node.start_position().row, |node| {
16262            node.start_position().row
16263        }) + 1,
16264        end_line: semicolon.end_position().row + 1,
16265    };
16266    Some(CppSentinelFragmentedClassTail {
16267        class_node: *class_node,
16268        template_node: *template_node,
16269        name: name.clone(),
16270        raw_supertypes: raw_supertypes.clone(),
16271        fragmented: FragmentedExportBody {
16272            reparse_start: *reparse_start,
16273            reparse_end,
16274            class_range,
16275        },
16276        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
16277    })
16278}
16279
16280/// Recover the class and out-of-line owner scopes from every malformed
16281/// namespace-sentinel region in `root`.
16282///
16283/// This is the shared structural counterpart to
16284/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
16285/// reuses the visitor's sentinel/class admission predicates instead of parsing
16286/// source text a second time.  The returned values own only ranges and names, so
16287/// they can be retained by an inverted usage scan after the tree borrow ends.
16288pub fn cpp_sentinel_recovered_classes(
16289    root: Node<'_>,
16290    source: &str,
16291) -> Vec<CppSentinelRecoveredClass> {
16292    if !root.has_error() {
16293        return Vec::new();
16294    }
16295    // This scan owns its walk of `root`, so it owns the parent index that walk
16296    // asks its ancestor questions through. Built after the error gate: a clean
16297    // tree returns without paying for one.
16298    let ancestry = ParentIndex::new(root);
16299    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
16300    let mut stack = vec![root];
16301    while let Some(current) = stack.pop() {
16302        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
16303            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
16304        {
16305            let namespace_components = cpp_sentinel_recovered_namespace_components(
16306                recovered.function,
16307                &recovered.namespace_components,
16308                source,
16309            );
16310            let fragmented = cpp_sentinel_fragmented_class_tail(
16311                recovered.function,
16312                recovered.body,
16313                source,
16314                &ancestry,
16315            );
16316            let mut class_candidates = Vec::new();
16317            let mut cursor = recovered.body.walk();
16318            for (class_node, template_node) in recovered
16319                .body
16320                .named_children(&mut cursor)
16321                .filter_map(cpp_sentinel_body_class_candidate)
16322            {
16323                let Some(name) = class_like_name(class_node, source, &ancestry) else {
16324                    continue;
16325                };
16326                if name.is_empty() || cpp_export_macro_token(&name) {
16327                    continue;
16328                }
16329                let is_fragmented = fragmented
16330                    .as_ref()
16331                    .is_some_and(|tail| same_node(tail.class_node, class_node));
16332                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
16333                    continue;
16334                }
16335                let class_range = if is_fragmented {
16336                    fragmented
16337                        .as_ref()
16338                        .map(|tail| tail.fragmented.class_range)
16339                        .expect("fragmented class range is present when class matches")
16340                } else {
16341                    cpp_declaration_range(template_node.unwrap_or(class_node))
16342                };
16343                class_candidates.push((class_range, name));
16344            }
16345            if let Some(fragmented) = fragmented
16346                .as_ref()
16347                .filter(|tail| tail.class_node.kind() == "ERROR")
16348            {
16349                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
16350            }
16351
16352            let mut owner_ranges =
16353                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
16354            cpp_sentinel_extend_unique_owner_ranges(
16355                &mut owner_ranges,
16356                cpp_sentinel_recovered_sibling_owner_ranges(
16357                    recovered.function,
16358                    &namespace_components,
16359                    source,
16360                ),
16361            );
16362            for (class_range, name) in class_candidates {
16363                push_cpp_sentinel_recovered_class(
16364                    &mut recovered_classes,
16365                    cpp_declaration_range(recovered.body),
16366                    &namespace_components,
16367                    class_range,
16368                    name,
16369                    &owner_ranges,
16370                );
16371            }
16372
16373            if let Some(declaration_list) = recovered
16374                .function
16375                .parent()
16376                .filter(|parent| parent.kind() == "declaration_list")
16377            {
16378                let outer_namespace =
16379                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
16380                push_cpp_sentinel_sibling_classes(
16381                    &mut recovered_classes,
16382                    declaration_list,
16383                    recovered.function,
16384                    &outer_namespace,
16385                    source,
16386                    &ancestry,
16387                );
16388            }
16389        } else if let Some(region) =
16390            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
16391        {
16392            let namespace_components = cpp_sentinel_recovered_namespace_components(
16393                current,
16394                &region.namespace_components,
16395                source,
16396            );
16397            let owner_container = current
16398                .parent()
16399                .filter(|parent| parent.kind() == "declaration_list")
16400                .unwrap_or(current);
16401            let owner_ranges =
16402                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
16403            push_cpp_sentinel_recovered_class(
16404                &mut recovered_classes,
16405                cpp_declaration_range(owner_container),
16406                &namespace_components,
16407                Range {
16408                    start_byte: region.class_start,
16409                    end_byte: region.class_close_end,
16410                    start_line: region.class_start_line,
16411                    end_line: region.class_close_line,
16412                },
16413                region.name,
16414                &owner_ranges,
16415            );
16416        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
16417            // A generic sentinel-prefixed class can be reduced as a malformed
16418            // function/ERROR without the explicit `namespace X` token pair.
16419            // Reuse the declaration visitor's bounded reparse and retain only
16420            // the recovered class identity/range here.
16421            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
16422                region;
16423            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
16424                continue;
16425            };
16426            let root = tree.root_node();
16427            let template_node = cpp_sentinel_reparsed_leading_template(root);
16428            // A region reparse is its own tree and needs its own parent index.
16429            let reparsed_ancestry = ParentIndex::new(root);
16430            let Some(reparsed_class) =
16431                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
16432            else {
16433                continue;
16434            };
16435            let class_node = reparsed_class.declaration_node;
16436            let name = reparsed_class.name;
16437            let namespace_components =
16438                cpp_sentinel_recovered_namespace_components(current, &[], source);
16439            let owner_container = current
16440                .parent()
16441                .filter(|parent| parent.kind() == "declaration_list")
16442                .unwrap_or(current);
16443            let mut owner_ranges =
16444                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
16445            cpp_sentinel_extend_unique_owner_ranges(
16446                &mut owner_ranges,
16447                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
16448            );
16449            push_cpp_sentinel_recovered_class(
16450                &mut recovered_classes,
16451                cpp_declaration_range(owner_container),
16452                &namespace_components,
16453                Range {
16454                    start_byte: class_start,
16455                    end_byte: close_end,
16456                    start_line: class_node.start_position().row + 1,
16457                    end_line: class_node.end_position().row + 1,
16458                },
16459                name,
16460                &owner_ranges,
16461            );
16462            if owner_container.kind() == "declaration_list" {
16463                push_cpp_sentinel_sibling_classes(
16464                    &mut recovered_classes,
16465                    owner_container,
16466                    current,
16467                    &namespace_components,
16468                    source,
16469                    &ancestry,
16470                );
16471            }
16472        }
16473
16474        let mut cursor = current.walk();
16475        stack.extend(current.named_children(&mut cursor));
16476    }
16477    // A shallower sentinel can expose nested classes as apparent namespace
16478    // siblings even after a deeper sentinel proves that a containing class
16479    // owns their ranges. Drop those shadow descriptors; scope recovery starts
16480    // from the proven containing class and appends parser-visible class
16481    // ancestors, preserving the full `Outer::Inner` chain.
16482    let shadowed = recovered_classes
16483        .iter()
16484        .map(|candidate| {
16485            recovered_classes.iter().any(|container| {
16486                container.class_range.start_byte <= candidate.class_range.start_byte
16487                    && container.class_range.end_byte >= candidate.class_range.end_byte
16488                    && container.class_range != candidate.class_range
16489                    && container.namespace_scope_components.len()
16490                        > candidate.namespace_scope_components.len()
16491                    && container
16492                        .namespace_scope_components
16493                        .starts_with(&candidate.namespace_scope_components)
16494            })
16495        })
16496        .collect::<Vec<_>>();
16497    let mut index = 0usize;
16498    recovered_classes.retain(|_| {
16499        let keep = !shadowed[index];
16500        index += 1;
16501        keep
16502    });
16503    recovered_classes
16504}
16505
16506/// A flat sentinel can swallow the first class while leaving later classes and
16507/// their out-of-line definitions as ordinary declaration-list siblings.  Once
16508/// the malformed class proves the sentinel envelope, retain those structurally
16509/// complete sibling classes under the same surviving namespace so every member
16510/// owner in the region uses one recovery contract.
16511fn push_cpp_sentinel_sibling_classes<'tree>(
16512    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16513    declaration_list: Node<'tree>,
16514    sentinel_node: Node<'tree>,
16515    namespace_components: &[String],
16516    source: &str,
16517    ancestry: &ParentIndex<'tree>,
16518) {
16519    let owner_ranges =
16520        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
16521    let namespace_range = cpp_declaration_range(declaration_list);
16522    let mut cursor = declaration_list.walk();
16523    for (class_node, template_node) in declaration_list
16524        .named_children(&mut cursor)
16525        .filter(|child| !same_node(*child, sentinel_node))
16526        .filter_map(cpp_sentinel_body_class_candidate)
16527    {
16528        let Some(name) = class_like_name(class_node, source, ancestry) else {
16529            continue;
16530        };
16531        if name.is_empty()
16532            || cpp_export_macro_token(&name)
16533            || cpp_complete_class_body_close(class_node).is_none()
16534        {
16535            continue;
16536        }
16537        push_cpp_sentinel_recovered_class(
16538            recovered_classes,
16539            namespace_range,
16540            namespace_components,
16541            cpp_declaration_range(template_node.unwrap_or(class_node)),
16542            name,
16543            &owner_ranges,
16544        );
16545    }
16546}
16547
16548fn push_cpp_sentinel_recovered_class(
16549    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
16550    namespace_range: Range,
16551    namespace_components: &[String],
16552    class_range: Range,
16553    name: String,
16554    owner_ranges: &[CppSentinelRecoveredOwner],
16555) {
16556    let mut scope_components = namespace_components.to_vec();
16557    scope_components.push(name);
16558    let owner_ranges = owner_ranges
16559        .iter()
16560        .filter(|owner| owner.scope_components.starts_with(&scope_components))
16561        .cloned()
16562        .collect::<Vec<_>>();
16563    if recovered_classes.iter().any(|existing| {
16564        existing.class_range == class_range && existing.scope_components == scope_components
16565    }) {
16566        return;
16567    }
16568    recovered_classes.push(CppSentinelRecoveredClass {
16569        namespace_range,
16570        namespace_scope_components: namespace_components.to_vec(),
16571        class_range,
16572        scope_components,
16573        owner_ranges,
16574    });
16575}
16576
16577fn cpp_sentinel_recovered_namespace_components(
16578    function: Node<'_>,
16579    recovered_components: &[String],
16580    source: &str,
16581) -> Vec<String> {
16582    let mut ancestor_components = Vec::new();
16583    let mut ancestor = function.parent();
16584    while let Some(current) = ancestor {
16585        if current.kind() == "namespace_definition"
16586            && let Some(name_node) = current.child_by_field_name("name")
16587            && let Some(components) = cpp_name_components(name_node, source)
16588        {
16589            ancestor_components.push(
16590                components
16591                    .into_iter()
16592                    .map(|component| component.name)
16593                    .collect::<Vec<_>>(),
16594            );
16595        }
16596        ancestor = current.parent();
16597    }
16598    ancestor_components.reverse();
16599    let mut ancestors = ancestor_components
16600        .into_iter()
16601        .flatten()
16602        .collect::<Vec<_>>();
16603
16604    let overlap = (0..=ancestors.len().min(recovered_components.len()))
16605        .rev()
16606        .find(|length| {
16607            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
16608        })
16609        .unwrap_or(0);
16610    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
16611    ancestors
16612}
16613
16614fn cpp_sentinel_recovered_owner_ranges(
16615    body: Node<'_>,
16616    namespace_components: &[String],
16617    source: &str,
16618) -> Vec<CppSentinelRecoveredOwner> {
16619    let mut owners = Vec::new();
16620    walk_named_tree_preorder(body, true, |node| {
16621        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
16622    });
16623    owners
16624}
16625
16626fn cpp_sentinel_collect_owner_range(
16627    node: Node<'_>,
16628    namespace_components: &[String],
16629    source: &str,
16630    owners: &mut Vec<CppSentinelRecoveredOwner>,
16631) -> WalkControl {
16632    if node.kind() != "function_definition" {
16633        return WalkControl::Continue;
16634    }
16635    let Some(function_declarator) = extract_function_declarator(node) else {
16636        return WalkControl::Continue;
16637    };
16638    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
16639        return WalkControl::Continue;
16640    };
16641    let Some(mut components) = cpp_name_components(name_node, source) else {
16642        return WalkControl::Continue;
16643    };
16644    if components.len() <= 1 {
16645        return WalkControl::Continue;
16646    }
16647    components.pop();
16648    let mut owner_components = components
16649        .into_iter()
16650        .map(|component| component.name)
16651        .collect::<Vec<_>>();
16652    let overlap = (0..=namespace_components.len().min(owner_components.len()))
16653        .rev()
16654        .find(|length| {
16655            owner_components[..*length]
16656                == namespace_components[namespace_components.len().saturating_sub(*length)..]
16657        })
16658        .unwrap_or(0);
16659    let mut scope_components = namespace_components.to_vec();
16660    scope_components.extend(owner_components.drain(overlap..));
16661    if scope_components.len() <= namespace_components.len() {
16662        return WalkControl::Continue;
16663    }
16664    let range = cpp_declaration_range(node);
16665    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
16666        existing.range == range && existing.scope_components == scope_components
16667    }) {
16668        owners.push(CppSentinelRecoveredOwner {
16669            range,
16670            owner_name_start_byte: name_node.start_byte(),
16671            namespace_component_count: namespace_components.len(),
16672            scope_components,
16673        });
16674    }
16675    WalkControl::Continue
16676}
16677
16678fn cpp_sentinel_extend_unique_owner_ranges(
16679    owners: &mut Vec<CppSentinelRecoveredOwner>,
16680    additional: Vec<CppSentinelRecoveredOwner>,
16681) {
16682    for owner in additional {
16683        if !owners.iter().any(|existing| {
16684            existing.range == owner.range && existing.scope_components == owner.scope_components
16685        }) {
16686            owners.push(owner);
16687        }
16688    }
16689}
16690
16691fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
16692    if node.kind() != "ERROR" || node.named_child_count() != 1 {
16693        return false;
16694    }
16695    let Some(end_name) = node.named_child(0) else {
16696        return false;
16697    };
16698    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
16699        return false;
16700    }
16701    let mut cursor = node.walk();
16702    node.children(&mut cursor)
16703        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
16704}
16705
16706/// Collect owner definitions that the malformed sentinel left as later
16707/// declaration-list siblings. Parser-visible namespace siblings are a hard
16708/// boundary: their declarations must keep their own lexical namespace.
16709fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16710    parent: Node<'_>,
16711    sentinel_node: Node<'_>,
16712    namespace_components: &[String],
16713    source: &str,
16714) -> Vec<CppSentinelRecoveredOwner> {
16715    let mut owners = Vec::new();
16716    let mut after_sentinel = false;
16717    let mut cursor = parent.walk();
16718    for child in parent.named_children(&mut cursor) {
16719        if !after_sentinel {
16720            if same_node(child, sentinel_node) {
16721                after_sentinel = true;
16722            }
16723            continue;
16724        }
16725        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
16726    }
16727    owners
16728}
16729
16730/// Collect owner definitions after a malformed namespace, stopping only at
16731/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
16732/// enclosing container is not trusted to belong to the recovered namespace.
16733fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16734    parent: Node<'_>,
16735    sentinel_node: Node<'_>,
16736    namespace_components: &[String],
16737    source: &str,
16738) -> Option<Vec<CppSentinelRecoveredOwner>> {
16739    let mut owners = Vec::new();
16740    let mut after_namespace = false;
16741    let mut cursor = parent.walk();
16742    for child in parent.named_children(&mut cursor) {
16743        if !after_namespace {
16744            if same_node(child, sentinel_node) {
16745                after_namespace = true;
16746            }
16747            continue;
16748        }
16749        if cpp_sentinel_namespace_end(child, source) {
16750            return Some(owners);
16751        }
16752        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
16753    }
16754    None
16755}
16756
16757fn cpp_sentinel_append_owner_ranges(
16758    sibling: Node<'_>,
16759    namespace_components: &[String],
16760    source: &str,
16761    owners: &mut Vec<CppSentinelRecoveredOwner>,
16762) {
16763    walk_named_tree_preorder(sibling, true, |node| {
16764        if node.kind() == "namespace_definition" {
16765            return WalkControl::SkipChildren;
16766        }
16767        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
16768    });
16769}
16770
16771fn cpp_sentinel_recovered_sibling_owner_ranges(
16772    sentinel_node: Node<'_>,
16773    namespace_components: &[String],
16774    source: &str,
16775) -> Vec<CppSentinelRecoveredOwner> {
16776    let Some(declaration_list) = sentinel_node
16777        .parent()
16778        .filter(|parent| parent.kind() == "declaration_list")
16779    else {
16780        return Vec::new();
16781    };
16782    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
16783        declaration_list,
16784        sentinel_node,
16785        namespace_components,
16786        source,
16787    );
16788
16789    let Some(namespace) = declaration_list
16790        .parent()
16791        .filter(|parent| parent.kind() == "namespace_definition")
16792    else {
16793        return owners;
16794    };
16795    let Some(outer_parent) = namespace.parent() else {
16796        return owners;
16797    };
16798    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
16799        outer_parent,
16800        namespace,
16801        namespace_components,
16802        source,
16803    ) {
16804        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
16805    }
16806    owners
16807}
16808
16809fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
16810    let mut current = function_declarator.child_by_field_name("declarator")?;
16811    loop {
16812        if let Some(name) = macro_decorated_unqualified_name(current) {
16813            current = name;
16814            continue;
16815        }
16816        if matches!(
16817            current.kind(),
16818            "qualified_identifier"
16819                | "scoped_identifier"
16820                | "scoped_type_identifier"
16821                | "identifier"
16822                | "field_identifier"
16823                | "operator_name"
16824                | "destructor_name"
16825                | "literal_operator_name"
16826        ) {
16827            return Some(current);
16828        }
16829        current = current
16830            .child_by_field_name("declarator")
16831            .or_else(|| current.child_by_field_name("name"))
16832            .or_else(|| last_named_child(current))?;
16833    }
16834}
16835
16836/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
16837/// `::` between the halves. The scope is an attribute-like macro token, not
16838/// a namespace; `Name` is the declared name.
16839///
16840/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
16841/// tree-sitter joins the attribute macro and the constructor name into one
16842/// `qualified_identifier` and marks the separator it had to invent as MISSING.
16843/// That flag is the grammar's own record that no separator is in the source,
16844/// so read it instead of looking for `::` in the node text. A genuine
16845/// `Outer::Inner` keeps a present separator and is declined here, and so is
16846/// the explicit-global `::name`, whose `::` is present and whose scope is
16847/// absent.
16848fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
16849    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
16850        return None;
16851    }
16852    let mut cursor = node.walk();
16853    if node
16854        .children(&mut cursor)
16855        .any(|child| child.kind() == "::" && !child.is_missing())
16856    {
16857        return None;
16858    }
16859    node.child_by_field_name("name")
16860}
16861
16862fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
16863    if let Some(name) = macro_decorated_unqualified_name(node) {
16864        return cpp_name_components(name, source);
16865    }
16866    match node.kind() {
16867        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16868            let mut components = match node.child_by_field_name("scope") {
16869                Some(scope) => cpp_name_components(scope, source)?,
16870                None => Vec::new(),
16871            };
16872            let name = node.child_by_field_name("name")?;
16873            components.push(canonical_cpp_qualified_component(name, source)?);
16874            Some(components)
16875        }
16876        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
16877    }
16878}
16879
16880fn cpp_sentinel_fragment_boundary<'tree>(
16881    function: Node<'tree>,
16882    class_node: Node<'tree>,
16883    class_body: Node<'tree>,
16884    source: &str,
16885) -> Option<(Node<'tree>, Node<'tree>)> {
16886    let declaration_list = function.parent()?;
16887    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
16888        return None;
16889    }
16890    let namespace = declaration_list.parent()?;
16891    if namespace.kind() != "namespace_definition"
16892        || namespace.child_by_field_name("body") != Some(declaration_list)
16893    {
16894        return None;
16895    }
16896    let mut cursor = declaration_list.walk();
16897    let closes = declaration_list
16898        .children(&mut cursor)
16899        .filter(|child| {
16900            !child.is_named()
16901                && child.kind() == "}"
16902                && child.start_byte() >= function.end_byte()
16903                && child.start_byte() > class_node.end_byte()
16904                && child.start_byte() > class_body.start_byte()
16905        })
16906        .collect::<Vec<_>>();
16907    let [close] = closes.as_slice() else {
16908        return None;
16909    };
16910    let semicolon = namespace.next_named_sibling()?;
16911    if !cpp_is_stray_semicolon(semicolon, source)
16912        || close.end_byte() != namespace.end_byte()
16913        || semicolon.start_byte() < namespace.end_byte()
16914    {
16915        return None;
16916    }
16917    Some((*close, semicolon))
16918}
16919
16920/// Detect the bogus declaration/function tree that tree-sitter recovers for a
16921/// region prefixed by an object-like macro sentinel the parser cannot see
16922/// (issue #941), and return the byte range `[start, end)` of the swallowed
16923/// declaration interior to reparse.
16924///
16925/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
16926/// `function_definition` whose first non-comment named child is the sentinel
16927/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
16928/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
16929/// the real items.
16930/// `start` is the end of the sentinel identifier -- everything after it is the
16931/// genuine source. `end` is the node's end, extended across any trailing empty
16932/// `;` statement the mis-parse displaced past the node (the class/struct closing
16933/// semicolon), so the reparse sees a complete, brace-balanced item.
16934///
16935/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
16936/// node (`has_error`). Unknown annotation/export macros can make a real callable
16937/// error-recovered even though tree-sitter still preserves its declarator, so a
16938/// preserved callable is admitted only when a displaced class keyword precedes
16939/// that declarator. The clean-reparse-to-items gate in
16940/// `cpp_reparsed_items_are_indexable` is the final arbiter.
16941/// Return the reparse start and, when present, the structurally recovered class
16942/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
16943/// separately from the reparse start because an opaque template-declaration
16944/// macro may precede it.
16945fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
16946    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
16947    {
16948        return None;
16949    }
16950    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
16951    // retain a valid function declarator despite the unknown export macro making
16952    // the outer node erroneous. Remember that declarator for the ordering gate
16953    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
16954    // class keyword precedes a spurious callable assembled from a later member.
16955    let mut declarator_cursor = node.walk();
16956    let preserved_callable = node
16957        .children_by_field_name("declarator", &mut declarator_cursor)
16958        .find_map(extract_function_declarator);
16959    // Leading documentation comments are attached to the malformed
16960    // `function_definition` as named children.  They are not part of the
16961    // sentinel prefix, so select the first non-comment child structurally
16962    // rather than requiring the sentinel to be child zero.  This is the shape
16963    // emitted for nlohmann/json's `basic_json`: its class documentation comment
16964    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
16965    // envelope otherwise ends at the first nested union.
16966    let mut cursor = node.walk();
16967    let first = node
16968        .named_children(&mut cursor)
16969        .find(|child| child.kind() != "comment")?;
16970    if first.kind() != "type_identifier" {
16971        return None;
16972    }
16973    let sentinel = normalize_cpp_whitespace(node_text(first, source));
16974    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
16975        return None;
16976    }
16977    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
16978    // makes the trailing sentinel of one region and the leading sentinel of the
16979    // next both land as bare macro-token identifiers ahead of the real content.
16980    // Advance past every leading macro-token identifier so the reparse begins at
16981    // genuine source rather than another sentinel that would re-form the bogus
16982    // shape and fail the reparse gate.
16983    let mut start = first.end_byte();
16984    let mut after_first = false;
16985    let mut cursor = node.walk();
16986    for child in node.named_children(&mut cursor) {
16987        if !after_first {
16988            if same_node(child, first) {
16989                after_first = true;
16990            }
16991            continue;
16992        }
16993        if matches!(child.kind(), "identifier" | "type_identifier")
16994            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
16995        {
16996            start = child.end_byte();
16997        } else {
16998            break;
16999        }
17000    }
17001    // An additional opaque template-declaration macro before a class can be
17002    // folded into the bogus function's qualified declarator.  In that shape
17003    // the macro is not a direct sibling we can skip above; tree-sitter exposes
17004    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
17005    // Reparse from that keyword (or a real preceding `template` keyword) so the
17006    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
17007    // a class nested in a genuine sentinel-wrapped namespace lies after the
17008    // body opening and must not change the established region start.
17009    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
17010    let mut class_start = None;
17011    let mut template_start = None;
17012    let mut stack = vec![node];
17013    while let Some(current) = stack.pop() {
17014        if current.start_byte() >= prefix_end {
17015            continue;
17016        }
17017        if matches!(
17018            current.kind(),
17019            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
17020        ) {
17021            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
17022                "class" | "struct" | "union" | "enum" => {
17023                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
17024                        seen.min(current.start_byte())
17025                    }));
17026                }
17027                "template" => {
17028                    template_start =
17029                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
17030                            seen.min(current.start_byte())
17031                        }));
17032                }
17033                _ => {}
17034            }
17035        }
17036        let mut cursor = current.walk();
17037        stack.extend(current.children(&mut cursor));
17038    }
17039    if preserved_callable.is_some_and(|callable| {
17040        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
17041    }) {
17042        return None;
17043    }
17044    if let Some(class_start) = class_start {
17045        start = template_start
17046            .filter(|template_start| *template_start < class_start)
17047            .unwrap_or(class_start);
17048    }
17049    Some((start, class_start))
17050}
17051
17052/// Locate a sentinel-prefixed class whose malformed declaration was split across
17053/// root-level siblings. The true class close is represented structurally as a
17054/// lone `}` error followed by the class's displaced `;`; nested method/body
17055/// errors are not direct siblings of the sentinel node and therefore cannot
17056/// satisfy this pair.
17057fn cpp_sentinel_macro_class_region<'tree>(
17058    node: Node<'tree>,
17059    source: &str,
17060) -> Option<(usize, usize, usize, usize, usize, usize)> {
17061    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
17062        return None;
17063    };
17064    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
17065        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
17066        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
17067    if class_start >= body_open_start {
17068        return None;
17069    }
17070    let sibling_close = {
17071        let mut sibling = node.next_named_sibling();
17072        let mut found = None;
17073        while let Some(current) = sibling {
17074            let next = current.next_named_sibling();
17075            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
17076                found = Some((
17077                    current.start_byte(),
17078                    semicolon.end_byte(),
17079                    semicolon.end_position().row + 1,
17080                ));
17081                break;
17082            }
17083            sibling = next;
17084        }
17085        found
17086    };
17087    // A stray `};` sibling is this class's close only when the bounded reparse
17088    // agrees the first body-bearing class ENDS there. When the malformed
17089    // envelope swallowed the class's true close, the scan can promote a much
17090    // later scope's close instead -- in protobuf-generated headers
17091    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
17092    // `struct TableStruct_*` paired with the first message class's `};`, making
17093    // the recovered "class body" span whole `namespace {}` blocks and minting
17094    // namespace-scope classes as nested members of the recovered class, which
17095    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
17096    // (#2275). On disagreement, fall through to the suffix-reparse boundary
17097    // below, which derives the close from the class node's own balanced body
17098    // range.
17099    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
17100        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17101            return false;
17102        };
17103        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
17104        // A region reparse is its own tree and needs its own parent index.
17105        let reparsed_ancestry = ParentIndex::new(tree.root_node());
17106        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
17107            tree.root_node(),
17108            template_node,
17109            source,
17110            &reparsed_ancestry,
17111        ) else {
17112            return false;
17113        };
17114        let body = reparsed_class.body;
17115        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
17116    });
17117    let (class_close_start, class_close_end, class_close_line) =
17118        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
17119            (class_close_start, class_close_end, class_close_line)
17120        } else {
17121            // When the malformed envelope itself is an ERROR, tree-sitter can
17122            // leave the class's balanced close in the source while promoting
17123            // all following members to siblings. Reparse the complete suffix
17124            // and use the first body-bearing class node's own field range as
17125            // the partition boundary. This keeps balancing in tree-sitter and
17126            // preserves the source's original byte offsets.
17127            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
17128            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
17129            // A region reparse is its own tree and needs its own parent index.
17130            let reparsed_ancestry = ParentIndex::new(tree.root_node());
17131            let reparsed_class = cpp_sentinel_reparsed_class(
17132                tree.root_node(),
17133                template_node,
17134                source,
17135                &reparsed_ancestry,
17136            )?;
17137            let body = reparsed_class.body;
17138            let class_close_end = body.end_byte();
17139            let class_close_start = class_close_end.checked_sub(1)?;
17140            let class_close_line = body.end_position().row + 1;
17141            (class_close_start, class_close_end, class_close_line)
17142        };
17143    if class_close_start <= class_start {
17144        return None;
17145    }
17146
17147    // Reparse only far enough to expose the class body opening. This is a
17148    // structured check that the candidate really begins with a body-bearing
17149    // class-like item; the original malformed tree cannot provide that node.
17150    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17151    let class_root = tree.root_node();
17152    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
17153    // A region reparse is its own tree and needs its own parent index.
17154    let reparsed_ancestry = ParentIndex::new(class_root);
17155    let reparsed_class =
17156        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
17157    let body = reparsed_class.body;
17158    // The class body opening must agree with the malformed wrapper's structured
17159    // body field. This rejects an inner nested class while permitting later
17160    // members to remain fragmented as root-level siblings in the bounded parse.
17161    if body.start_byte() != body_open_start {
17162        return None;
17163    }
17164    let body_start = body.start_byte().checked_add(1)?;
17165    (body_start < class_close_start).then_some((
17166        reparse_start,
17167        class_start,
17168        body_start,
17169        class_close_start,
17170        class_close_end,
17171        class_close_line,
17172    ))
17173}
17174
17175/// Find the `{` token immediately following the class/struct/union/enum token
17176/// at `class_start` in the malformed tree. The token is anonymous in the C++
17177/// grammar, so this deliberately walks all children (not only named children)
17178/// and relies on sibling structure rather than source-text searching.
17179fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
17180    let mut stack = vec![node];
17181    while let Some(current) = stack.pop() {
17182        if current.start_byte() == class_start
17183            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
17184        {
17185            let mut sibling = current.next_sibling();
17186            while let Some(candidate) = sibling {
17187                if candidate.kind() == "{" {
17188                    return Some(candidate.start_byte());
17189                }
17190                sibling = candidate.next_sibling();
17191            }
17192        }
17193        let mut cursor = current.walk();
17194        stack.extend(current.children(&mut cursor));
17195    }
17196    None
17197}
17198
17199/// The class body that tree-sitter displaced out of a sentinel-prefixed
17200/// declaration and left as the malformed node's next sibling.
17201///
17202/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
17203/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
17204/// the last child of that `ERROR` and its `{` opens a sibling
17205/// `compound_statement` instead. The body is still the malformed tree's own
17206/// structured token, which is what the caller's `body.start_byte() !=
17207/// body_open_start` agreement check needs; it just is not reachable by walking
17208/// forward from the class token inside the node.
17209fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
17210    node.next_named_sibling()
17211        .filter(|sibling| sibling.kind() == "compound_statement")
17212}
17213
17214fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
17215    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
17216    let mut end = if class_start.is_some() {
17217        cpp_macro_prefixed_class_end(source, start)?
17218    } else {
17219        node.end_byte()
17220    };
17221    if class_start.is_none()
17222        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
17223    {
17224        end = end.max(namespace_end);
17225    }
17226    let mut sibling = node.next_named_sibling();
17227    while let Some(current) = sibling {
17228        if !cpp_is_stray_semicolon(current, source) {
17229            break;
17230        }
17231        end = current.end_byte();
17232        sibling = current.next_named_sibling();
17233    }
17234    (start < end).then_some((start, end))
17235}
17236
17237/// Extend a sentinel reparse through a following namespace that tree-sitter
17238/// flattened into the sentinel node's sibling list.
17239///
17240/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
17241/// unknown macro becomes a false function return type and consumes the first
17242/// namespace body. A second `namespace detail` then loses its enclosing node:
17243/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
17244/// attaches its declarations to the surrounding error tree. Reparse from that
17245/// structured keyword so tree-sitter, rather than a source-text brace scan,
17246/// supplies the complete namespace boundary.
17247fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
17248    let mut sibling = node.next_sibling();
17249    let keyword = loop {
17250        let candidate = sibling?;
17251        sibling = candidate.next_sibling();
17252        if candidate.kind() != "comment" {
17253            break candidate;
17254        }
17255    };
17256    if keyword.kind() != "namespace" {
17257        return None;
17258    }
17259    let name = loop {
17260        let candidate = sibling?;
17261        sibling = candidate.next_sibling();
17262        if candidate.kind() != "comment" {
17263            break candidate;
17264        }
17265    };
17266    if cpp_namespace_name_components(name, source).is_empty() {
17267        return None;
17268    }
17269    let open = loop {
17270        let candidate = sibling?;
17271        sibling = candidate.next_sibling();
17272        if candidate.kind() != "comment" {
17273            break candidate;
17274        }
17275    };
17276    if open.kind() != "{" {
17277        return None;
17278    }
17279
17280    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
17281    let root = tree.root_node();
17282    let mut cursor = root.walk();
17283    let namespace = root
17284        .named_children(&mut cursor)
17285        .find(|candidate| candidate.kind() != "comment")?;
17286    (namespace.kind() == "namespace_definition"
17287        && namespace.start_byte() == keyword.start_byte()
17288        && namespace.child_by_field_name("body").is_some())
17289    .then_some(namespace.end_byte())
17290}
17291
17292/// Parse the source suffix beginning at a structurally recovered class/template
17293/// keyword and return the end of its first body-bearing class item.  The parser,
17294/// rather than a brace scanner, owns nested-body balancing.  This is needed when
17295/// the original error tree truncates the class and scatters later members as
17296/// top-level siblings.
17297fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
17298    let tree = cpp_reparse_region_items(source, start, source.len())?;
17299    let root = tree.root_node();
17300    let mut cursor = root.walk();
17301    for item in root.named_children(&mut cursor) {
17302        if item.end_byte() <= start || item.kind() == "comment" {
17303            continue;
17304        }
17305        let mut stack = vec![item];
17306        while let Some(current) = stack.pop() {
17307            if matches!(
17308                current.kind(),
17309                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
17310            ) && cpp_body_node(current).is_some()
17311            {
17312                return Some(current.end_byte());
17313            }
17314            let mut cursor = current.walk();
17315            stack.extend(current.named_children(&mut cursor));
17316        }
17317        // The recovered prefix is required to begin with the class item.  If
17318        // the first real item is something else, fail closed rather than skip
17319        // arbitrary source looking for a later class.
17320        return None;
17321    }
17322    None
17323}
17324
17325/// An empty `;` statement: the displaced closing semicolon of a struct/class that
17326/// the sentinel mis-parse split off past the bogus function node.
17327fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
17328    node.kind() == "expression_statement"
17329        && node.named_child_count() == 0
17330        && node_text(node, source).trim() == ";"
17331}
17332
17333fn cpp_stray_close_followed_by_semicolon<'tree>(
17334    close: Node<'tree>,
17335    next: Option<Node<'tree>>,
17336    source: &str,
17337) -> Option<Node<'tree>> {
17338    cpp_is_stray_close_brace(close, source)
17339        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
17340        .flatten()
17341}
17342
17343/// Recover the member that follows CPython's object header macro when
17344/// tree-sitter folds both declarations into one malformed field.
17345///
17346/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
17347/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
17348/// sole child is the real field declarator. The tail may wrap that identifier
17349/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
17350/// malformed wrappers do not provide the same evidence that this is the
17351/// CPython macro boundary.
17352#[derive(Clone, Copy)]
17353pub(crate) struct RecoveredPyObjectHeadField<'tree> {
17354    pub(crate) type_node: Node<'tree>,
17355    pub(crate) name: Node<'tree>,
17356    pub(crate) declarator: Node<'tree>,
17357}
17358
17359impl RecoveredPyObjectHeadField<'_> {
17360    pub(crate) fn pointer_depth(self) -> i32 {
17361        let mut depth = 0;
17362        let mut current = self.declarator;
17363        while current != self.name {
17364            debug_assert_eq!(current.kind(), "pointer_declarator");
17365            depth += 1;
17366            current = current
17367                .child_by_field_name("declarator")
17368                .expect("recovered PyObject field pointer has an inner declarator");
17369        }
17370        depth
17371    }
17372}
17373
17374pub(crate) fn recovered_pyobject_head_field<'tree>(
17375    node: Node<'tree>,
17376    source: &str,
17377) -> Option<RecoveredPyObjectHeadField<'tree>> {
17378    if node.kind() != "field_declaration" {
17379        return None;
17380    }
17381    let type_node = node.child_by_field_name("type")?;
17382    if type_node.kind() != "type_identifier"
17383        || node_text(type_node, source).trim() != "PyObject_HEAD"
17384    {
17385        return None;
17386    }
17387    let pseudo_declarator = node.child_by_field_name("declarator")?;
17388    let mut cursor = node.walk();
17389    let errors = node
17390        .named_children(&mut cursor)
17391        .filter(|child| child.kind() == "ERROR")
17392        .collect::<Vec<_>>();
17393    let [error] = errors.as_slice() else {
17394        return None;
17395    };
17396    if error.named_child_count() != 1 {
17397        return None;
17398    }
17399    let error_child = error.named_child(0)?;
17400    if pseudo_declarator.kind() == "field_identifier"
17401        && error.start_byte() >= pseudo_declarator.end_byte()
17402        && error_child.kind() == "identifier"
17403    {
17404        return Some(RecoveredPyObjectHeadField {
17405            type_node: pseudo_declarator,
17406            name: error_child,
17407            declarator: error_child,
17408        });
17409    }
17410    if pseudo_declarator.kind() != "pointer_declarator"
17411        || error.end_byte() > pseudo_declarator.start_byte()
17412        || error_child.kind() != "identifier"
17413    {
17414        return None;
17415    }
17416    let mut name = pseudo_declarator;
17417    while name.kind() == "pointer_declarator" {
17418        name = name.child_by_field_name("declarator")?;
17419    }
17420    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
17421        type_node: error_child,
17422        name,
17423        declarator: pseudo_declarator,
17424    })
17425}
17426
17427/// Recover the real field name when a leading object-like annotation macro
17428/// displaces a qualified type into tree-sitter's bit-field recovery shape.
17429///
17430/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
17431/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
17432/// `bitfield_clause` containing an error plus an assignment.  The assignment's
17433/// left field is the only structured declaration name in that malformed tail.
17434/// A real bit-field is excluded by the all-caps macro type and required error.
17435fn recovered_macro_qualified_field_clause<'tree>(
17436    node: Node<'tree>,
17437    source: &str,
17438) -> Option<Node<'tree>> {
17439    if node.kind() != "field_declaration" {
17440        return None;
17441    }
17442    let macro_type = node.child_by_field_name("type")?;
17443    if macro_type.kind() != "type_identifier"
17444        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17445    {
17446        return None;
17447    }
17448    let mut cursor = node.walk();
17449    node.named_children(&mut cursor)
17450        .find(|child| child.kind() == "bitfield_clause")
17451}
17452
17453fn recovered_macro_qualified_field_declarators<'tree>(
17454    node: Node<'tree>,
17455    source: &str,
17456) -> Option<Vec<Node<'tree>>> {
17457    let pseudo_declarator = node.child_by_field_name("declarator")?;
17458    if pseudo_declarator.kind() != "field_identifier" {
17459        return None;
17460    }
17461    let clause = recovered_macro_qualified_field_clause(node, source)?;
17462    if !(0..clause.named_child_count()).any(|index| {
17463        clause
17464            .named_child(index)
17465            .is_some_and(|child| child.kind() == "ERROR")
17466    }) {
17467        return None;
17468    }
17469    let mut recovered = Vec::new();
17470    let mut stack = vec![clause];
17471    while let Some(current) = stack.pop() {
17472        if current.kind() == "assignment_expression"
17473            && let Some(left) = current.child_by_field_name("left")
17474            && extract_variable_name(left, source).is_some()
17475        {
17476            recovered.push(left);
17477            break;
17478        }
17479        let mut cursor = current.walk();
17480        stack.extend(current.named_children(&mut cursor));
17481    }
17482    if recovered.is_empty() {
17483        return None;
17484    }
17485    let mut cursor = node.walk();
17486    recovered.extend(
17487        node.children_by_field_name("declarator", &mut cursor)
17488            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
17489    );
17490    Some(recovered)
17491}
17492
17493/// Recover a macro-qualified constructor that tree-sitter represents as one
17494/// field declaration. The constructor call remains inside the direct recovery
17495/// error, while each member initializer becomes a false function declarator.
17496/// The class owner proves the constructor name and lets the caller ignore those
17497/// initializer declarators.
17498fn recovered_macro_qualified_constructor_call<'tree>(
17499    node: Node<'tree>,
17500    class_name: &str,
17501    source: &str,
17502) -> Option<Node<'tree>> {
17503    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
17504    let error = bitfield
17505        .named_child(0)
17506        .filter(|child| child.kind() == "ERROR")?;
17507    let mut stack = vec![error];
17508    while let Some(current) = stack.pop() {
17509        if current.kind() == "call_expression"
17510            && current
17511                .child_by_field_name("function")
17512                .is_some_and(|function| node_text(function, source) == class_name)
17513            && current
17514                .child_by_field_name("arguments")
17515                .is_some_and(|arguments| arguments.kind() == "argument_list")
17516        {
17517            return Some(current);
17518        }
17519        let mut cursor = current.walk();
17520        stack.extend(current.named_children(&mut cursor));
17521    }
17522    None
17523}
17524
17525/// Recover a macro-qualified member function declaration that tree-sitter
17526/// represents as a pseudo-field. An object-like export macro before a qualified
17527/// return type can displace the namespace and type into an ERROR/bitfield
17528/// recovery, leaving the callable as a structured `call_expression`.
17529///
17530/// The caller must route this shape before ordinary declarator classification;
17531/// otherwise the displaced namespace identifier is published as a field.
17532fn recovered_macro_qualified_function_call<'tree>(
17533    node: Node<'tree>,
17534    source: &str,
17535) -> Option<Node<'tree>> {
17536    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
17537    let declarator = node.child_by_field_name("declarator")?;
17538    if declarator.kind() != "field_identifier" {
17539        return None;
17540    }
17541    let mut cursor = node.walk();
17542    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
17543    if !named.iter().any(|child| {
17544        child.kind() == "storage_class_specifier"
17545            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
17546    }) {
17547        return None;
17548    }
17549    let mut bitfield_cursor = bitfield.walk();
17550    let payload = bitfield
17551        .named_children(&mut bitfield_cursor)
17552        .collect::<Vec<_>>();
17553    let [displaced_error, call] = payload.as_slice() else {
17554        return None;
17555    };
17556    if displaced_error.kind() != "ERROR"
17557        || displaced_error.named_child_count() != 1
17558        || displaced_error
17559            .named_child(0)
17560            .is_none_or(|child| child.kind() != "identifier")
17561        || call.kind() != "call_expression"
17562        || call
17563            .child_by_field_name("function")
17564            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
17565        || call
17566            .child_by_field_name("arguments")
17567            .is_none_or(|arguments| arguments.kind() != "argument_list")
17568    {
17569        return None;
17570    }
17571    Some(*call)
17572}
17573
17574fn recovered_macro_qualified_function_parameters(
17575    arguments: Node<'_>,
17576    source: &str,
17577) -> Option<(String, Vec<String>)> {
17578    if arguments.kind() != "argument_list" {
17579        return None;
17580    }
17581    let mut cursor = arguments.walk();
17582    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
17583    if named.is_empty() {
17584        return Some(("()".to_string(), Vec::new()));
17585    }
17586    let mut types = Vec::new();
17587    let mut labels = Vec::new();
17588    let mut index = 0;
17589    while index < named.len() {
17590        let parameter_type = named[index];
17591        let parameter_name = named.get(index + 1).copied()?;
17592        if !matches!(
17593            parameter_type.kind(),
17594            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
17595        ) || parameter_name.kind() != "ERROR"
17596            || parameter_name.named_child_count() != 1
17597            || parameter_name
17598                .named_child(0)
17599                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
17600        {
17601            return None;
17602        }
17603        let parameter_name = parameter_name.named_child(0)?;
17604        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
17605        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
17606        index += 2;
17607    }
17608    Some((format!("({})", types.join(", ")), labels))
17609}
17610
17611/// Recognize the phantom field tree-sitter emits for a macro-qualified
17612/// function return type.  For example,
17613/// `static API result_type ThresholdForSmallA() { ... }` can become a
17614/// `field_declaration` (`API` as the type and `result_type` as a field name)
17615/// followed by a clean `function_definition` for `ThresholdForSmallA`.
17616///
17617/// Keep this predicate entirely tied to the CST envelope: the type must be an
17618/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
17619/// the declaration must carry a missing semicolon rather than a real one, and
17620/// the immediate named sibling must expose a function declarator.  A real
17621/// macro-decorated field with an explicit semicolon therefore remains a field.
17622pub fn recovered_macro_return_type_node<'tree>(
17623    node: Node<'tree>,
17624    source: &str,
17625) -> Option<Node<'tree>> {
17626    if node.kind() != "field_declaration" {
17627        return None;
17628    }
17629    let macro_type = node.child_by_field_name("type")?;
17630    if macro_type.kind() != "type_identifier"
17631        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
17632    {
17633        return None;
17634    }
17635    let declarator = node.child_by_field_name("declarator")?;
17636    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
17637        return None;
17638    }
17639    let mut has_missing_semicolon = false;
17640    let mut has_real_semicolon = false;
17641    for child in children_iter(node) {
17642        if child.kind() != ";" {
17643            continue;
17644        }
17645        if child.is_missing() {
17646            has_missing_semicolon = true;
17647        } else {
17648            has_real_semicolon = true;
17649        }
17650    }
17651    if !has_missing_semicolon || has_real_semicolon {
17652        return None;
17653    }
17654    let mut next = node.next_named_sibling();
17655    while next.is_some_and(|sibling| sibling.kind() == "comment") {
17656        next = next.and_then(|sibling| sibling.next_named_sibling());
17657    }
17658    let next = next?;
17659    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
17660        return None;
17661    }
17662    let function_declarator = next.child_by_field_name("declarator")?;
17663    extract_function_declarator(function_declarator).map(|_| declarator)
17664}
17665
17666/// Whether `name` is a type parameter of a template declaration that lexically
17667/// encloses `node`. The malformed macro-return field uses the parameter name as
17668/// its pseudo-declarator; preserving that field is necessary to publish a
17669/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
17670/// ancestors instead of interpreting source text so nested templates and
17671/// parser-recovered regions retain their real lexical scopes.
17672pub(crate) fn cpp_active_template_type_parameter<'tree>(
17673    node: Node<'tree>,
17674    name: &str,
17675    source: &str,
17676    ancestry: &ParentIndex<'tree>,
17677) -> bool {
17678    let mut ancestor = ancestry.parent(node);
17679    while let Some(current) = ancestor {
17680        if current.kind() == "template_declaration"
17681            && let Some(parameters) = current.child_by_field_name("parameters")
17682            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
17683                cpp_template_parameter_name(parameter, source)
17684            })
17685            .type_parameter
17686        {
17687            return true;
17688        }
17689        ancestor = ancestry.parent(current);
17690    }
17691    false
17692}
17693
17694/// Reparse the region `[start, end)` of `source` as C++, confined to the region
17695/// via included ranges so every reparsed node keeps its original byte offset and
17696/// line number. The existing visitors read node text from the original source,
17697/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
17698/// `parse_rust_region_tree` technique.
17699fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
17700    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
17701}
17702
17703fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
17704    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
17705        return None;
17706    }
17707    let semicolon = node.next_sibling()?;
17708    if semicolon.kind() != ";" || semicolon.is_missing() {
17709        return None;
17710    }
17711    let row = node.start_position().row;
17712    let mut start = node.start_byte();
17713    let mut sibling = node.prev_sibling();
17714    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
17715        if previous.kind() == ";" {
17716            break;
17717        }
17718        start = previous.start_byte();
17719        sibling = previous.prev_sibling();
17720    }
17721    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
17722}
17723
17724/// One `RET name _(( args ));`-style K&R prototype-macro invocation
17725/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
17726/// byte spans a clean reparse needs, in original-source order.
17727struct PrototypeMacroCandidate {
17728    /// Where the return type and declared name begin.
17729    run_start: usize,
17730    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
17731    /// exclusive end of the first reparse span.
17732    identifier_start: usize,
17733    /// The inner `(`'s start byte: the start of the second reparse span.
17734    /// This paren is kept so the reparse sees exactly one parameter-list
17735    /// paren pair.
17736    inner_open_start: usize,
17737    /// Byte just past the inner `)`: the end of the second reparse span.
17738    inner_close_end: usize,
17739    /// Byte just past the outer `)`: the start of the third reparse span.
17740    outer_close_end: usize,
17741    /// Byte just past the terminating `;`: the end of the third reparse
17742    /// span, and of the whole candidate.
17743    semicolon_end: usize,
17744}
17745
17746impl PrototypeMacroCandidate {
17747    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
17748    /// one included-range tree: the return type and name, the parenthesized
17749    /// argument list, and the terminating `;`. The macro token and its outer
17750    /// wrapping parenthesis are omitted -- deleting them is exactly what the
17751    /// (absent) preprocessor macro expansion would do.
17752    fn ranges(&self) -> [(usize, usize); 3] {
17753        [
17754            (self.run_start, self.identifier_start),
17755            (self.inner_open_start, self.inner_close_end),
17756            (self.outer_close_end, self.semicolon_end),
17757        ]
17758    }
17759}
17760
17761/// A live terminating semicolon owned directly by `node`.
17762fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
17763    node.child(node.child_count().checked_sub(1)?)
17764        .filter(|child| child.kind() == ";" && !child.is_missing())
17765}
17766
17767/// The known pre-ANSI prototype macros whose expansion is exactly their one
17768/// argument. The malformed CST cannot prove that an arbitrary identifier has
17769/// that definition, so unknown spellings fail closed rather than turning an
17770/// ordinary broken declaration into a Function (issue #2932).
17771fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
17772    matches!(
17773        node.kind(),
17774        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
17775    ) && matches!(
17776        normalize_cpp_whitespace(node_text(node, source)).as_str(),
17777        "_" | "__P" | "OF" | "PROTO"
17778    )
17779}
17780
17781/// Read tree-sitter's recovery for `declared_name MACRO`: a
17782/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
17783/// declared name, and whose `name` is a known prototype macro.
17784fn cpp_prototype_macro_qualified_parts<'tree>(
17785    node: Node<'tree>,
17786    source: &str,
17787) -> Option<(Node<'tree>, Node<'tree>)> {
17788    if node.kind() != "qualified_identifier" {
17789        return None;
17790    }
17791    let declared_name = node
17792        .child_by_field_name("scope")
17793        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
17794    let macro_name = macro_decorated_unqualified_name(node)?;
17795    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
17796}
17797
17798/// The inner parenthesized node of tree-sitter's structured
17799/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
17800/// the macro invocation's outer parentheses while retaining the one pair an
17801/// ordinary function declarator needs. No delimiter search is involved: both
17802/// pairs and their ownership come from the CST.
17803fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
17804    if arguments.kind() != "argument_list"
17805        || arguments.named_child_count() != 1
17806        || arguments.child_count() != 3
17807        || arguments
17808            .child(0)
17809            .is_none_or(|open| open.kind() != "(" || open.is_missing())
17810        || arguments
17811            .child(2)
17812            .is_none_or(|close| close.kind() != ")" || close.is_missing())
17813    {
17814        return None;
17815    }
17816    let inner = arguments.named_child(0)?;
17817    let close_index = match inner.kind() {
17818        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
17819        // tree-sitter completes the C++ cast production with a zero-width
17820        // value after the live close parenthesis.
17821        "cast_expression" => inner.child_count().checked_sub(2)?,
17822        _ => return None,
17823    };
17824    (inner
17825        .child(0)
17826        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
17827        && inner
17828            .child(close_index)
17829            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
17830    .then_some(inner)
17831}
17832
17833fn cpp_prototype_macro_candidate_from_init_declaration(
17834    declaration: Node<'_>,
17835    source: &str,
17836) -> Option<PrototypeMacroCandidate> {
17837    let init = declaration
17838        .child_by_field_name("declarator")
17839        .filter(|declarator| declarator.kind() == "init_declarator")?;
17840    let malformed_declarator = init.child_by_field_name("declarator")?;
17841    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
17842        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
17843    } else {
17844        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
17845            return None;
17846        }
17847        let declared_name_error = init
17848            .prev_named_sibling()
17849            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
17850        let declared_name = declared_name_error
17851            .named_child(0)
17852            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
17853        (declared_name, malformed_declarator)
17854    };
17855    let arguments = init
17856        .child_by_field_name("value")
17857        .filter(|value| value.kind() == "argument_list")?;
17858    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17859    let semicolon = cpp_direct_semicolon(declaration)?;
17860    let return_type = declaration.child_by_field_name("type")?;
17861    if return_type.end_byte() > declared_name.start_byte()
17862        || declared_name.end_byte() > macro_name.start_byte()
17863        || macro_name.end_byte() > arguments.start_byte()
17864        || arguments.end_byte() > semicolon.start_byte()
17865    {
17866        return None;
17867    }
17868    Some(cpp_prototype_macro_candidate_from_parts(
17869        declaration.start_byte(),
17870        macro_name,
17871        inner,
17872        arguments,
17873        semicolon,
17874    ))
17875}
17876
17877fn cpp_prototype_macro_candidate_from_qualified_declaration(
17878    declaration: Node<'_>,
17879    source: &str,
17880) -> Option<PrototypeMacroCandidate> {
17881    let qualified = declaration
17882        .child_by_field_name("declarator")
17883        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
17884    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17885    let open_error = qualified
17886        .next_named_sibling()
17887        .filter(|next| next.kind() == "ERROR")?;
17888    let close_error = last_named_child(declaration)
17889        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
17890    if open_error.child_count() < 3
17891        || open_error
17892            .child(0)
17893            .is_none_or(|open| open.kind() != "(" || open.is_missing())
17894        || open_error
17895            .child(1)
17896            .is_none_or(|open| open.kind() != "(" || open.is_missing())
17897        || close_error.child_count() != 2
17898        || close_error
17899            .child(0)
17900            .is_none_or(|close| close.kind() != ")" || close.is_missing())
17901        || close_error
17902            .child(1)
17903            .is_none_or(|close| close.kind() != ")" || close.is_missing())
17904    {
17905        return None;
17906    }
17907    let inner_open = open_error.child(1)?;
17908    let inner_close = close_error.child(0)?;
17909    let outer_close = close_error.child(1)?;
17910    let semicolon = cpp_direct_semicolon(declaration)?;
17911    let return_type = declaration.child_by_field_name("type")?;
17912    if return_type.end_byte() > qualified.start_byte()
17913        || macro_name.end_byte() > open_error.start_byte()
17914        || inner_open.start_byte() > inner_close.end_byte()
17915        || inner_close.end_byte() > outer_close.start_byte()
17916        || outer_close.end_byte() > semicolon.start_byte()
17917    {
17918        return None;
17919    }
17920    Some(PrototypeMacroCandidate {
17921        run_start: declaration.start_byte(),
17922        identifier_start: macro_name.start_byte(),
17923        inner_open_start: inner_open.start_byte(),
17924        inner_close_end: inner_close.end_byte(),
17925        outer_close_end: outer_close.end_byte(),
17926        semicolon_end: semicolon.end_byte(),
17927    })
17928}
17929
17930fn cpp_prototype_macro_candidate_from_pointer_expression(
17931    statement: Node<'_>,
17932    source: &str,
17933) -> Option<PrototypeMacroCandidate> {
17934    if statement.kind() != "expression_statement"
17935        || statement.named_child_count() != 1
17936        || !statement.has_error()
17937    {
17938        return None;
17939    }
17940    let expansion = statement
17941        .named_child(0)
17942        .filter(|child| child.kind() == "parameter_pack_expansion")?;
17943    let binary = expansion
17944        .child_by_field_name("pattern")
17945        .filter(|pattern| pattern.kind() == "binary_expression")?;
17946    if binary.child_count() != 3
17947        || binary
17948            .child(1)
17949            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
17950        || expansion
17951            .child(expansion.child_count().checked_sub(1)?)
17952            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
17953    {
17954        return None;
17955    }
17956    let return_type = binary.child_by_field_name("left")?;
17957    let call = binary
17958        .child_by_field_name("right")
17959        .filter(|right| right.kind() == "call_expression")?;
17960    let qualified = call.child_by_field_name("function")?;
17961    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
17962    let arguments = call
17963        .child_by_field_name("arguments")
17964        .filter(|arguments| arguments.kind() == "argument_list")?;
17965    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
17966    let semicolon = cpp_direct_semicolon(statement)?;
17967    if return_type.end_byte() > declared_name.start_byte()
17968        || macro_name.end_byte() > arguments.start_byte()
17969        || arguments.end_byte() > semicolon.start_byte()
17970    {
17971        return None;
17972    }
17973    Some(cpp_prototype_macro_candidate_from_parts(
17974        statement.start_byte(),
17975        macro_name,
17976        inner,
17977        arguments,
17978        semicolon,
17979    ))
17980}
17981
17982fn cpp_prototype_macro_candidate_from_parts(
17983    run_start: usize,
17984    macro_name: Node<'_>,
17985    inner_arguments: Node<'_>,
17986    outer_arguments: Node<'_>,
17987    semicolon: Node<'_>,
17988) -> PrototypeMacroCandidate {
17989    PrototypeMacroCandidate {
17990        run_start,
17991        identifier_start: macro_name.start_byte(),
17992        inner_open_start: inner_arguments.start_byte(),
17993        inner_close_end: inner_arguments.end_byte(),
17994        outer_close_end: outer_arguments.end_byte(),
17995        semicolon_end: semicolon.end_byte(),
17996    }
17997}
17998
17999/// Recover only CST shapes whose fields preserve the declaration name, macro
18000/// invocation, nested argument list, and terminator. Arbitrary flattened
18001/// `ERROR` wreckage stays unsupported rather than being interpreted through a
18002/// token or delimiter scan (issue #2932).
18003fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
18004    let candidate = match node.kind() {
18005        "declaration" if node.has_error() => {
18006            cpp_prototype_macro_candidate_from_init_declaration(node, source)
18007                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
18008        }
18009        "expression_statement" => {
18010            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
18011        }
18012        _ => None,
18013    };
18014    candidate.into_iter().collect()
18015}
18016
18017fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
18018    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
18019        return false;
18020    }
18021    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
18022        return false;
18023    }
18024    let Some(declarator) = (if node.kind() == "function_definition" {
18025        node.child_by_field_name("declarator")
18026            .and_then(extract_function_declarator)
18027    } else {
18028        node.named_child(0)
18029            .filter(|child| child.kind() == "function_declarator")
18030    }) else {
18031        return false;
18032    };
18033    let Some(name) = cpp_function_declarator_name_node(declarator) else {
18034        return false;
18035    };
18036    declarator.start_byte() == node.start_byte()
18037        && name.kind() == "identifier"
18038        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18039}
18040
18041/// Reparse a fragmented class-body interior while preserving its original byte
18042/// and line offsets, confined to the region by tree-sitter included ranges.
18043///
18044/// This used to materialize the region's whole file prefix as whitespace and
18045/// make the lexer walk it, the technique #1309 replaced on the other reparse
18046/// path: O(file) per fragmented-class recovery, on files that are already
18047/// error-recovered and already slow (#2788). Included ranges give the parser
18048/// the same view -- the region's bytes, at their original offsets and
18049/// line/column positions -- without materializing or lexing anything before it.
18050///
18051/// The equality of the two views is the claim, so a debug build parses both and
18052/// asserts the trees agree node for node.
18053fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
18054    let region = cpp_reparse_region_items(source, start, end);
18055
18056    #[cfg(debug_assertions)]
18057    assert_eq!(
18058        region.as_ref().map(cpp_tree_shape),
18059        cpp_reparse_padded_class_body(source, start, end)
18060            .as_ref()
18061            .map(cpp_tree_shape),
18062        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
18063         prefix produces"
18064    );
18065
18066    region
18067}
18068
18069/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
18070/// replaces, kept as the oracle a debug build asserts every region reparse
18071/// against and as the release-mode parity tests' reference (#2788).
18072#[cfg(any(debug_assertions, test))]
18073fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
18074    if start >= end {
18075        // The included-range parser refuses an empty region; the padded one
18076        // returned a tree holding nothing, which every caller read as "no
18077        // members here".
18078        return None;
18079    }
18080    let bytes = source.as_bytes();
18081    let prefix = bytes.get(..start)?;
18082    let interior = bytes.get(start..end)?;
18083    let mut padded = Vec::with_capacity(end);
18084    padded.extend(
18085        prefix
18086            .iter()
18087            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
18088    );
18089    padded.extend_from_slice(interior);
18090    let padded = String::from_utf8(padded).ok()?;
18091    let mut parser = tree_sitter::Parser::new();
18092    parser
18093        .set_language(&tree_sitter_cpp::LANGUAGE.into())
18094        .ok()?;
18095    parser.parse(&padded, None)
18096}
18097
18098/// Every node of `tree` in preorder, by kind, span and position, which is what
18099/// two reparses of one region have to agree on for their callers to read the
18100/// same declarations out of either (#2788).
18101#[cfg(any(debug_assertions, test))]
18102fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
18103    let mut shape = Vec::new();
18104    let mut cursor = tree.root_node().walk();
18105    let mut stack = vec![tree.root_node()];
18106    while let Some(node) = stack.pop() {
18107        shape.push((
18108            node.kind(),
18109            node.start_byte(),
18110            node.end_byte(),
18111            node.start_position().row,
18112            node.start_position().column,
18113            node.is_named(),
18114            node.is_missing(),
18115        ));
18116        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
18117        stack.extend(children.into_iter().rev());
18118    }
18119    shape
18120}
18121
18122/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
18123/// reparsed interior is indexed only when every top-level named node is a
18124/// well-formed C++ item (or a comment) and at least one real item is present.
18125/// Expression/statement soup surfaces as a top-level `ERROR` or
18126/// `expression_statement`, neither of which is an item kind, so it is rejected.
18127///
18128/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
18129/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
18130/// swallowed by a preceding dangling sentinel) reparses to a real
18131/// `namespace_definition` whose body still holds a bogus `function_definition`,
18132/// so the subtree legitimately carries an error. Container items are admitted
18133/// even with an internal error; the inner bogus function is recovered recursively
18134/// when `visit_function_definition` walks it. Each recursion strips at least one
18135/// leading sentinel, so the region strictly shrinks and recovery terminates.
18136///
18137/// A top-level `function_definition` is the one place we stay strict: it is
18138/// admitted only when it is clean or is itself a sentinel candidate. A function
18139/// that has an error and is not a sentinel is a real callable with a broken body,
18140/// so we refuse the whole reparse and let the ordinary path handle it (preserving
18141/// its real return type rather than re-deriving an implicit one).
18142fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
18143    let mut cursor = root.walk();
18144    let mut saw_item = false;
18145    for child in root.named_children(&mut cursor) {
18146        match child.kind() {
18147            "comment" => {}
18148            "function_definition" => {
18149                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
18150                    return false;
18151                }
18152                saw_item = true;
18153            }
18154            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
18155            _ => return false,
18156        }
18157    }
18158    saw_item
18159}
18160
18161/// Robustness gate for a reparsed fragmented multiple-base export class body
18162/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
18163/// kinds a class body produces when reparsed at translation-unit scope: the
18164/// access-specifier label preceding the first member surfaces as a
18165/// `labeled_statement` wrapping that member, and members surface as
18166/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
18167/// Statement or expression soup surfaces as other top-level kinds and is rejected,
18168/// so only a genuinely member-shaped body is ever re-owned as members; anything
18169/// ambiguous falls back to indexing the class alone.
18170fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
18171    if node.kind() != "ERROR" {
18172        return false;
18173    }
18174    let mut stack = Vec::new();
18175    let mut saw_function_declarator = false;
18176    let mut cursor = node.walk();
18177    for child in node.named_children(&mut cursor) {
18178        stack.push(child);
18179    }
18180    while let Some(current) = stack.pop() {
18181        match current.kind() {
18182            // Tree-sitter may wrap adjacent copy-control declarations in a
18183            // nested ERROR. Keep descending only through ERROR wrappers; the
18184            // actual declaration payload must be a function_declarator.
18185            "ERROR" => {
18186                let mut cursor = current.walk();
18187                stack.extend(current.named_children(&mut cursor));
18188            }
18189            "function_declarator" => saw_function_declarator = true,
18190            _ => return false,
18191        }
18192    }
18193    saw_function_declarator
18194}
18195
18196fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
18197    if node.kind() != "ERROR" {
18198        return false;
18199    }
18200    let mut cursor = node.walk();
18201    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18202    let [explicit, constructor_error, destructor] = named.as_slice() else {
18203        return false;
18204    };
18205    let Some(constructor) = constructor_error.named_child(0) else {
18206        return false;
18207    };
18208    let Some(constructor_name) =
18209        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
18210    else {
18211        return false;
18212    };
18213    let Some(destructor_name) =
18214        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
18215    else {
18216        return false;
18217    };
18218    let Some(destroyed_type) = destructor_name.named_child(0) else {
18219        return false;
18220    };
18221    explicit.kind() == "explicit_function_specifier"
18222        && constructor_error.kind() == "ERROR"
18223        && constructor_error.named_child_count() == 1
18224        && constructor.kind() == "function_declarator"
18225        && constructor_name.kind() == "identifier"
18226        && destructor.kind() == "function_declarator"
18227        && destructor_name.kind() == "destructor_name"
18228        && destroyed_type.kind() == "identifier"
18229        && node_text(constructor_name, source) == node_text(destroyed_type, source)
18230}
18231
18232fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
18233    if node.kind() != "compound_statement" {
18234        return false;
18235    }
18236    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
18237        return false;
18238    };
18239    if prefix.kind() == "labeled_statement"
18240        && prefix.named_child(0).is_some_and(|label| {
18241            matches!(
18242                node_text(label, source).trim(),
18243                "public" | "private" | "protected"
18244            )
18245        })
18246    {
18247        return prefix.named_children(&mut prefix.walk()).any(|child| {
18248            child.kind() == "declaration"
18249                && child.has_error()
18250                && child
18251                    .named_children(&mut child.walk())
18252                    .any(cpp_reparsed_member_error_is_indexable)
18253        });
18254    }
18255    // A malformed constructor initializer can be split into a declaration
18256    // followed by its compound body when the class prefix already contains
18257    // realistic members. Keep this admission tied to that exact structured
18258    // declaration/error/body chain rather than accepting arbitrary blocks.
18259    prefix.kind() == "declaration"
18260        && prefix.has_error()
18261        && prefix
18262            .named_children(&mut prefix.walk())
18263            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
18264}
18265
18266fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
18267    if !cpp_reparsed_member_error_is_indexable(node) {
18268        return false;
18269    }
18270    let Some(preproc) = node.next_named_sibling() else {
18271        return false;
18272    };
18273    preproc.kind() == "preproc_if"
18274        && preproc.has_error()
18275        && preproc
18276            .named_children(&mut preproc.walk())
18277            .any(|child| child.kind() == "expression_statement" && child.has_error())
18278        && preproc
18279            .next_named_sibling()
18280            .is_some_and(|body| body.kind() == "compound_statement")
18281}
18282
18283/// Return a function body whose braces and ownership are explicit in the
18284/// reparsed class-member tree. An error below a real function envelope is
18285/// recoverable by the ordinary function visitor; a missing/deferred body is
18286/// not, because accepting it would let statement soup masquerade as a member.
18287fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
18288    if node.kind() != "function_definition" {
18289        return None;
18290    }
18291    let body = node.child_by_field_name("body")?;
18292    if body.kind() != "compound_statement" {
18293        return None;
18294    }
18295    let open = body.child(0)?;
18296    let close = body.child(body.child_count().checked_sub(1)?)?;
18297    if open.kind() != "{"
18298        || open.is_missing()
18299        || close.kind() != "}"
18300        || close.is_missing()
18301        || close.end_byte() != body.end_byte()
18302        || body.end_byte() != node.end_byte()
18303    {
18304        return None;
18305    }
18306    Some(body)
18307}
18308
18309fn cpp_reparsed_member_function_errors_are_in_body(
18310    node: Node<'_>,
18311    body: Node<'_>,
18312    source: &str,
18313) -> bool {
18314    let mut cursor = node.walk();
18315    node.children(&mut cursor).all(|child| {
18316        same_node(child, body)
18317            || cpp_reparsed_member_attribute_error(child, source)
18318            || cpp_reparsed_member_signature_identifier_errors(child)
18319            || (!child.has_error() && !child.is_error() && !child.is_missing())
18320    })
18321}
18322
18323/// A complete callable can still carry parser errors in its signature when a
18324/// project annotation is not part of the C++ grammar (`nonneg int`,
18325/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
18326/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
18327/// leaves inside the already-proven callable envelope; structured statements,
18328/// literals, missing tokens, and other malformed signature payload remain
18329/// rejected.
18330fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
18331    if !node.has_error() && !node.is_error() && !node.is_missing() {
18332        return false;
18333    }
18334    let mut stack = vec![node];
18335    let mut saw_error = false;
18336    while let Some(current) = stack.pop() {
18337        if current.is_missing() {
18338            return false;
18339        }
18340        if current.kind() == "ERROR" {
18341            saw_error = true;
18342            let mut cursor = current.walk();
18343            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
18344            if children
18345                .iter()
18346                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
18347            {
18348                return false;
18349            }
18350            stack.extend(children);
18351            continue;
18352        }
18353        let mut cursor = current.walk();
18354        stack.extend(current.children(&mut cursor));
18355    }
18356    saw_error
18357}
18358
18359fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
18360    node.kind() == "ERROR"
18361        && node.named_child_count() == 1
18362        && node
18363            .named_child(0)
18364            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
18365}
18366
18367fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
18368    node.kind() == "identifier"
18369        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
18370}
18371
18372/// A C++ attribute placed between a member's declarator and body can make
18373/// tree-sitter expose the callable as
18374/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
18375/// Keep this admission tied to that exact node geometry. In particular, an
18376/// arbitrary ERROR or identifier before a compound statement is not enough.
18377fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
18378    let Some(body) = cpp_reparsed_member_function_body(node) else {
18379        return false;
18380    };
18381    let mut cursor = node.walk();
18382    let named = node
18383        .named_children(&mut cursor)
18384        .filter(|child| child.kind() != "comment")
18385        .collect::<Vec<_>>();
18386    let [type_node, error, attribute, body_node] = named.as_slice() else {
18387        return false;
18388    };
18389    if !same_node(*body_node, body)
18390        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18391        || !cpp_reparsed_export_macro_identifier(*attribute, source)
18392        || error.kind() != "ERROR"
18393        || error.named_child_count() != 1
18394    {
18395        return false;
18396    }
18397    error
18398        .named_child(0)
18399        .is_some_and(cpp_reparsed_attribute_callable_declarator)
18400}
18401
18402fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
18403    cpp_structured_type_path(node, source).is_some()
18404        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
18405}
18406
18407fn cpp_reparsed_member_return_and_declarator_are_indexable(
18408    return_error: Node<'_>,
18409    declarator: Node<'_>,
18410    body_node: Node<'_>,
18411    body: Node<'_>,
18412    source: &str,
18413) -> bool {
18414    return_error.kind() == "ERROR"
18415        && return_error.named_child_count() == 1
18416        && same_node(body_node, body)
18417        && return_error.named_child(0).is_some_and(|return_type| {
18418            cpp_reparsed_member_return_type_is_indexable(return_type, source)
18419        })
18420        && extract_function_declarator(declarator)
18421            .and_then(cpp_function_declarator_name_node)
18422            .is_some()
18423}
18424
18425fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18426    let Some(body) = cpp_reparsed_member_function_body(node) else {
18427        return false;
18428    };
18429    let mut cursor = node.walk();
18430    let named = node
18431        .named_children(&mut cursor)
18432        .filter(|child| child.kind() != "comment")
18433        .collect::<Vec<_>>();
18434    let [friend, return_error, declarator, body_node] = named.as_slice() else {
18435        return false;
18436    };
18437    friend.kind() == "type_identifier"
18438        && node_text(*friend, source) == "friend"
18439        && cpp_reparsed_member_return_and_declarator_are_indexable(
18440            *return_error,
18441            *declarator,
18442            *body_node,
18443            body,
18444            source,
18445        )
18446}
18447
18448fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18449    let Some(body) = cpp_reparsed_member_function_body(node) else {
18450        return false;
18451    };
18452    let mut cursor = node.walk();
18453    let named = node
18454        .named_children(&mut cursor)
18455        .filter(|child| child.kind() != "comment")
18456        .collect::<Vec<_>>();
18457    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
18458        return false;
18459    };
18460    prefix
18461        .iter()
18462        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
18463        && attribute.kind() == "type_identifier"
18464        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18465        && cpp_reparsed_member_return_and_declarator_are_indexable(
18466            *return_error,
18467            *declarator,
18468            *body_node,
18469            body,
18470            source,
18471        )
18472}
18473
18474/// An included-range reparse that begins inside a malformed class can merge an
18475/// access label and following template member. Tree-sitter then emits the label
18476/// as the `template_type` name, the template parameter list as its arguments,
18477/// an ERROR-wrapped return type, the callable declarator, and its complete
18478/// body. Admit only that exact structured displacement.
18479fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
18480    let Some(body) = cpp_reparsed_member_function_body(node) else {
18481        return false;
18482    };
18483    let mut cursor = node.walk();
18484    let named = node
18485        .named_children(&mut cursor)
18486        .filter(|child| child.kind() != "comment")
18487        .collect::<Vec<_>>();
18488    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
18489        return false;
18490    };
18491    let Some(template_name) = template_type.child_by_field_name("name") else {
18492        return false;
18493    };
18494    let Some(arguments) = template_type.child_by_field_name("arguments") else {
18495        return false;
18496    };
18497    let Some(return_type) = return_error.named_child(0) else {
18498        return false;
18499    };
18500    let mut cursor = template_type.walk();
18501    let template_errors = template_type
18502        .named_children(&mut cursor)
18503        .filter(|child| child.kind() == "ERROR")
18504        .collect::<Vec<_>>();
18505    let [comment_error] = template_errors.as_slice() else {
18506        return false;
18507    };
18508    let mut cursor = comment_error.walk();
18509    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
18510    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
18511        return false;
18512    };
18513    same_node(*body_node, body)
18514        && template_type.kind() == "template_type"
18515        && template_name.kind() == "type_identifier"
18516        && matches!(
18517            node_text(template_name, source).trim(),
18518            "public" | "private" | "protected"
18519        )
18520        && arguments.kind() == "template_argument_list"
18521        && arguments.named_child_count() > 0
18522        && !arguments.has_error()
18523        && !colon.is_named()
18524        && colon.kind() == ":"
18525        && comments.iter().all(|child| child.kind() == "comment")
18526        && !template_keyword.is_named()
18527        && template_keyword.kind() == "template"
18528        && return_error.kind() == "ERROR"
18529        && return_error.named_child_count() == 1
18530        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
18531        && extract_function_declarator(*declarator)
18532            .and_then(cpp_function_declarator_name_node)
18533            .is_some()
18534}
18535
18536/// Return the constructor declaration tree-sitter can merge into an access
18537/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
18538/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
18539/// declaration's apparent type; the callable name must still exactly match the
18540/// recovered class, so unrelated labeled statements are never re-owned.
18541fn cpp_reparsed_preprocessor_constructor<'tree>(
18542    node: Node<'tree>,
18543    class_name: &str,
18544    source: &str,
18545) -> Option<Node<'tree>> {
18546    if node.kind() != "labeled_statement" {
18547        return None;
18548    }
18549    let mut cursor = node.walk();
18550    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18551    let [label, directive_error, declaration] = named.as_slice() else {
18552        return None;
18553    };
18554    if label.kind() != "statement_identifier"
18555        || !matches!(
18556            node_text(*label, source),
18557            "public" | "private" | "protected"
18558        )
18559        || directive_error.kind() != "ERROR"
18560        || directive_error.child_count() != 1
18561        || directive_error
18562            .child(0)
18563            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
18564        || declaration.kind() != "declaration"
18565        || declaration.named_child_count() != 2
18566    {
18567        return None;
18568    }
18569    let apparent_type = declaration.child_by_field_name("type")?;
18570    if apparent_type.kind() != "type_identifier"
18571        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
18572    {
18573        return None;
18574    }
18575    let declarator = declaration.child_by_field_name("declarator")?;
18576    let function = extract_function_declarator(declarator)?;
18577    let name = cpp_function_declarator_name_node(function)?;
18578    (node_text(name, source) == class_name).then_some(*declaration)
18579}
18580
18581fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
18582    if extract_function_declarator(node)
18583        .and_then(cpp_function_declarator_name_node)
18584        .is_some()
18585    {
18586        return true;
18587    }
18588    node.kind() == "init_declarator"
18589        && node
18590            .child_by_field_name("declarator")
18591            .is_some_and(|declarator| declarator.kind() == "identifier")
18592        && node
18593            .child_by_field_name("value")
18594            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
18595}
18596
18597/// Return true for the constrained/attribute form that tree-sitter splits into
18598/// an ERROR declaration, a preprocessor `requires` clause, and a following
18599/// compound statement. The three nodes must remain immediate named siblings;
18600/// this deliberately does not search source text or skip unrelated statements.
18601fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
18602    if node.kind() != "ERROR" || node.named_child_count() != 3 {
18603        return false;
18604    }
18605    let mut cursor = node.walk();
18606    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18607    let [type_node, function_declarator, attribute] = named.as_slice() else {
18608        return false;
18609    };
18610    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
18611        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
18612        || attribute.kind() != "identifier"
18613        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
18614    {
18615        return false;
18616    }
18617    let Some(preproc) =
18618        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18619    else {
18620        return false;
18621    };
18622    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
18623        .filter(|sibling| sibling.kind() == "compound_statement")
18624    else {
18625        return false;
18626    };
18627    let Some(open) = body.child(0) else {
18628        return false;
18629    };
18630    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
18631        return false;
18632    };
18633    let Some(condition) = preproc.child_by_field_name("condition") else {
18634        return false;
18635    };
18636    let mut cursor = preproc.walk();
18637    let payload = preproc
18638        .named_children(&mut cursor)
18639        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
18640        .collect::<Vec<_>>();
18641    let [requires_statement] = payload.as_slice() else {
18642        return false;
18643    };
18644    let requires_clause = requires_statement.named_child(0);
18645
18646    open.kind() == "{"
18647        && !open.is_missing()
18648        && close.kind() == "}"
18649        && !close.is_missing()
18650        && close.end_byte() == body.end_byte()
18651        && requires_statement.kind() == "expression_statement"
18652        && requires_statement.named_child_count() == 1
18653        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
18654}
18655
18656fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18657    let mut sibling = node.next_named_sibling();
18658    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18659        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
18660    }
18661    sibling
18662}
18663
18664fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
18665    let mut sibling = node.prev_named_sibling();
18666    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
18667        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
18668    }
18669    sibling
18670}
18671
18672fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
18673    let Some(preproc) =
18674        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
18675    else {
18676        return false;
18677    };
18678    let Some(error) =
18679        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
18680    else {
18681        return false;
18682    };
18683    cpp_reparsed_attribute_requires_error(error, source)
18684}
18685
18686fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
18687    parameter: Node<'tree>,
18688    macro_name: Node<'tree>,
18689    message: Node<'tree>,
18690    source: &str,
18691) -> Option<Node<'tree>> {
18692    let parameter_name = parameter.named_child(0)?;
18693    (parameter.kind() == "type_parameter_declaration"
18694        && parameter_name.kind() == "type_identifier"
18695        && macro_name.kind() == "type_identifier"
18696        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
18697        && message.kind() == "string_literal")
18698        .then_some(parameter_name)
18699}
18700
18701fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
18702    node: Node<'tree>,
18703    source: &str,
18704) -> Option<Node<'tree>> {
18705    if node.kind() != "ERROR" {
18706        return None;
18707    }
18708    let mut cursor = node.walk();
18709    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18710    let [parameter, macro_name, message] = named.as_slice() else {
18711        return None;
18712    };
18713    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
18714}
18715
18716/// Recognize the alternate constraint-macro prefix where tree-sitter retains
18717/// the complete qualified constraint as a fourth child instead of moving it
18718/// into the following function. Keep the gate tied to a two-type template
18719/// constraint that names the declared type parameter.
18720fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
18721    node: Node<'tree>,
18722    source: &str,
18723) -> Option<Node<'tree>> {
18724    if node.kind() != "ERROR" {
18725        return None;
18726    }
18727    let mut cursor = node.walk();
18728    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18729    let [parameter, macro_name, message, constraint] = named.as_slice() else {
18730        return None;
18731    };
18732    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
18733        *parameter,
18734        *macro_name,
18735        *message,
18736        source,
18737    )?;
18738    let constraint_scope = constraint.child_by_field_name("scope")?;
18739    let constraint_template = constraint.child_by_field_name("name")?;
18740    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18741    let mut argument_cursor = constraint_arguments.walk();
18742    let constraint_types = constraint_arguments
18743        .named_children(&mut argument_cursor)
18744        .collect::<Vec<_>>();
18745    if constraint.kind() != "qualified_identifier"
18746        || constraint_scope.kind() != "namespace_identifier"
18747        || !matches!(
18748            constraint_template.kind(),
18749            "template_function" | "template_type"
18750        )
18751        || !matches!(constraint_types.as_slice(), [left, right]
18752            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18753        || constraint_arguments.has_error()
18754    {
18755        return None;
18756    }
18757    let parameter_text = node_text(parameter_name, source);
18758    let mut stack = constraint_types;
18759    while let Some(current) = stack.pop() {
18760        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
18761            return Some(parameter_name);
18762        }
18763        let mut cursor = current.walk();
18764        stack.extend(current.named_children(&mut cursor));
18765    }
18766    None
18767}
18768
18769fn cpp_reparsed_template_macro_companion_is_indexable(
18770    node: Node<'_>,
18771    parameter_name: Node<'_>,
18772    source: &str,
18773) -> bool {
18774    let Some(body) = cpp_reparsed_member_function_body(node) else {
18775        return false;
18776    };
18777    let mut cursor = node.walk();
18778    let named = node
18779        .named_children(&mut cursor)
18780        .filter(|child| child.kind() != "comment")
18781        .collect::<Vec<_>>();
18782    let [
18783        constraint,
18784        close_error,
18785        storage,
18786        return_error,
18787        declarator,
18788        body_node,
18789    ] = named.as_slice()
18790    else {
18791        return false;
18792    };
18793    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
18794        return false;
18795    };
18796    let Some(constraint_template) = constraint.child_by_field_name("name") else {
18797        return false;
18798    };
18799    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
18800        return false;
18801    };
18802    let Some(return_type) = return_error.named_child(0) else {
18803        return false;
18804    };
18805    let mut cursor = constraint_arguments.walk();
18806    let constraint_types = constraint_arguments
18807        .named_children(&mut cursor)
18808        .collect::<Vec<_>>();
18809    same_node(*body_node, body)
18810        && constraint.kind() == "qualified_identifier"
18811        && constraint_scope.kind() == "namespace_identifier"
18812        && constraint_template.kind() == "template_type"
18813        && matches!(constraint_types.as_slice(), [left, right]
18814            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18815        && !constraint_arguments.has_error()
18816        && close_error.kind() == "ERROR"
18817        && close_error.named_child_count() == 0
18818        && storage.kind() == "storage_class_specifier"
18819        && return_error.kind() == "ERROR"
18820        && return_error.named_child_count() == 1
18821        && return_type.kind() == "identifier"
18822        && node_text(return_type, source) == node_text(parameter_name, source)
18823        && extract_function_declarator(*declarator)
18824            .and_then(cpp_function_declarator_name_node)
18825            .is_some()
18826}
18827
18828fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
18829    node: Node<'tree>,
18830    parameter_name: Node<'_>,
18831    source: &str,
18832) -> Option<Node<'tree>> {
18833    let body = cpp_reparsed_member_function_body(node)?;
18834    let constraint = node.child_by_field_name("type")?;
18835    let constraint_template = constraint.child_by_field_name("name")?;
18836    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
18837    let mut argument_cursor = constraint_arguments.walk();
18838    let constraint_types = constraint_arguments
18839        .named_children(&mut argument_cursor)
18840        .collect::<Vec<_>>();
18841    if constraint.kind() != "qualified_identifier"
18842        || constraint_template.kind() != "template_type"
18843        || !matches!(constraint_types.as_slice(), [left, right]
18844            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
18845        || constraint_arguments.has_error()
18846        || node
18847            .child_by_field_name("body")
18848            .is_none_or(|candidate| !same_node(candidate, body))
18849    {
18850        return None;
18851    }
18852
18853    let mut cursor = node.walk();
18854    let recovery_errors = node
18855        .named_children(&mut cursor)
18856        .filter(|child| child.kind() == "ERROR")
18857        .collect::<Vec<_>>();
18858    if !recovery_errors
18859        .iter()
18860        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
18861        || !recovery_errors.iter().all(|error| {
18862            error.named_child_count() == 0
18863                || cpp_reparsed_constraint_macro_error(*error, source)
18864                || (error.named_child_count() == 1
18865                    && error
18866                        .named_child(0)
18867                        .is_some_and(|child| child.kind() == "function_declarator"))
18868        })
18869    {
18870        return None;
18871    }
18872
18873    let parameter_text = node_text(parameter_name, source);
18874    let mut declarators = node
18875        .child_by_field_name("declarator")
18876        .and_then(extract_function_declarator)
18877        .into_iter()
18878        .collect::<Vec<_>>();
18879    for error in recovery_errors {
18880        let mut stack = vec![error];
18881        while let Some(current) = stack.pop() {
18882            if current.kind() == "function_declarator" {
18883                declarators.push(current);
18884            }
18885            let mut cursor = current.walk();
18886            stack.extend(current.named_children(&mut cursor));
18887        }
18888    }
18889    declarators.into_iter().find(|declarator| {
18890        cpp_function_declarator_name_node(*declarator)
18891            .is_some_and(|name| name.kind() == "identifier")
18892            && declarator
18893                .child_by_field_name("parameters")
18894                .is_some_and(|parameters| {
18895                    parameters
18896                        .named_children(&mut parameters.walk())
18897                        .filter_map(|parameter| parameter.child_by_field_name("type"))
18898                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
18899                })
18900    })
18901}
18902
18903fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
18904    node: Node<'_>,
18905    parameter_name: Node<'_>,
18906    source: &str,
18907) -> bool {
18908    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
18909}
18910
18911fn cpp_reparsed_template_macro_function_companion_is_indexable(
18912    node: Node<'_>,
18913    parameter_name: Node<'_>,
18914    source: &str,
18915) -> bool {
18916    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
18917        return false;
18918    }
18919    let Some(return_type) = node.child_by_field_name("type") else {
18920        return false;
18921    };
18922    let Some(function_declarator) = node
18923        .child_by_field_name("declarator")
18924        .and_then(extract_function_declarator)
18925    else {
18926        return false;
18927    };
18928    if cpp_function_declarator_name_node(function_declarator).is_none()
18929        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
18930    {
18931        return false;
18932    }
18933    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
18934        return false;
18935    };
18936    let parameter_text = node_text(parameter_name, source);
18937    parameters
18938        .named_children(&mut parameters.walk())
18939        .any(|parameter| {
18940            parameter
18941                .child_by_field_name("type")
18942                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
18943        })
18944}
18945
18946fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
18947    if node.kind() != "ERROR" {
18948        return false;
18949    }
18950    let mut stack = vec![node];
18951    while let Some(current) = stack.pop() {
18952        let macro_shape = match current.kind() {
18953            "call_expression" => current
18954                .child_by_field_name("function")
18955                .zip(current.child_by_field_name("arguments")),
18956            "init_declarator" => current
18957                .child_by_field_name("declarator")
18958                .zip(current.child_by_field_name("value")),
18959            _ => None,
18960        };
18961        if let Some((name, arguments)) = macro_shape
18962            && name.kind() == "identifier"
18963            && arguments.kind() == "argument_list"
18964            && arguments.named_child_count() >= 2
18965            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
18966        {
18967            return true;
18968        }
18969        let mut cursor = current.walk();
18970        stack.extend(current.named_children(&mut cursor));
18971    }
18972    false
18973}
18974
18975fn cpp_recovered_template_macro_constructor<'tree>(
18976    node: Node<'tree>,
18977    source: &str,
18978) -> Option<(Node<'tree>, Node<'tree>)> {
18979    let mut prefix = node.prev_named_sibling()?;
18980    while prefix.kind() == "comment" {
18981        prefix = prefix.prev_named_sibling()?;
18982    }
18983    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
18984    let parameter = parameter_name
18985        .parent()
18986        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
18987    let declarator =
18988        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
18989    Some((declarator, parameter))
18990}
18991
18992fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
18993    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
18994        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
18995            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
18996                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
18997                    function,
18998                    parameter_name,
18999                    source,
19000                )
19001        });
19002    }
19003    let Some(parameter_name) =
19004        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
19005    else {
19006        return false;
19007    };
19008    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
19009        cpp_reparsed_template_macro_function_companion_is_indexable(
19010            function,
19011            parameter_name,
19012            source,
19013        )
19014    })
19015}
19016
19017fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
19018    let function_name = node
19019        .child_by_field_name("declarator")
19020        .and_then(extract_function_declarator)
19021        .and_then(cpp_function_declarator_name_node);
19022    if let Some(body) = cpp_reparsed_member_function_body(node)
19023        && function_name.is_some()
19024        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
19025    {
19026        return true;
19027    }
19028    cpp_reparsed_attribute_member_function(node, source)
19029        || cpp_reparsed_friend_function_is_indexable(node, source)
19030        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
19031        || cpp_reparsed_access_template_function_is_indexable(node, source)
19032        || cpp_recovered_template_macro_constructor(node, source).is_some()
19033}
19034
19035/// Recognize the three top-level nodes produced when an unknown attribute
19036/// macro separates an inline member's declarator from its body in a reparsed
19037/// class interior: an errorful declaration with a missing semicolon, the macro
19038/// call expression, and the complete compound body. Their adjacency and exact
19039/// structured shapes prove one recoverable member envelope; arbitrary calls or
19040/// blocks do not pass this gate.
19041fn cpp_reparsed_macro_attribute_member_sequence(
19042    children: &[Node<'_>],
19043    index: usize,
19044    source: &str,
19045) -> bool {
19046    let Some(prefix) = children.get(index).copied() else {
19047        return false;
19048    };
19049    let declaration = if prefix.kind() == "labeled_statement" {
19050        prefix
19051            .named_child(prefix.named_child_count().saturating_sub(1))
19052            .filter(|child| child.kind() == "declaration")
19053    } else {
19054        (prefix.kind() == "declaration").then_some(prefix)
19055    };
19056    let Some(declaration) = declaration else {
19057        return false;
19058    };
19059    if !declaration.has_error()
19060        || declaration
19061            .child_by_field_name("declarator")
19062            .and_then(extract_function_declarator)
19063            .and_then(cpp_function_declarator_name_node)
19064            .is_none()
19065    {
19066        return false;
19067    }
19068    let Some(attribute_statement) = children.get(index + 1).copied() else {
19069        return false;
19070    };
19071    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
19072        .then(|| attribute_statement.named_child(0))
19073        .flatten()
19074        .filter(|child| child.kind() == "call_expression")
19075    else {
19076        return false;
19077    };
19078    let Some(attribute_name) = attribute_call
19079        .child_by_field_name("function")
19080        .filter(|function| function.kind() == "identifier")
19081        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
19082    else {
19083        return false;
19084    };
19085    if !cpp_export_macro_token(&attribute_name) {
19086        return false;
19087    }
19088    let Some(body) = children.get(index + 2).copied() else {
19089        return false;
19090    };
19091    body.kind() == "compound_statement"
19092        && body.child(0).is_some_and(|open| open.kind() == "{")
19093        && body
19094            .child(body.child_count().saturating_sub(1))
19095            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
19096        && declaration.end_byte() <= attribute_statement.start_byte()
19097        && attribute_statement.end_byte() <= body.start_byte()
19098}
19099
19100/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
19101/// types and specifiers followed by a declarator, over and over, with nothing
19102/// left over. A string-argument attribute macro is what strands them there, and
19103/// the walk indexes exactly what this reads, so admitting the region is safe
19104/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
19105/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
19106/// the whole class body.
19107fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
19108    if node.kind() != "ERROR" {
19109        return false;
19110    }
19111    let run = stranded_declaration_run(node, source);
19112    run.complete && !run.declarations.is_empty()
19113}
19114
19115/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
19116/// translation-unit-shaped class-body reparse as an assignment expression:
19117/// `Ctor() = default`. Admit only the exact assignment shape, and only when
19118/// the same reparse retains a separate constructor declarator for that name.
19119/// The second condition keeps an arbitrary expression statement from becoming
19120/// a member merely because it contains a callee followed by `default`.
19121fn cpp_reparsed_defaulted_constructor_is_indexable(
19122    node: Node<'_>,
19123    members: &[Node<'_>],
19124    class_name: Option<&str>,
19125    source: &str,
19126) -> bool {
19127    let Some(class_name) = class_name else {
19128        return false;
19129    };
19130    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
19131        return false;
19132    }
19133    let Some(assignment) = node.named_child(0) else {
19134        return false;
19135    };
19136    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
19137        return false;
19138    }
19139    if assignment
19140        .child(1)
19141        .is_none_or(|operator| operator.kind() != "=")
19142    {
19143        return false;
19144    }
19145    let Some(call) = assignment.child_by_field_name("left") else {
19146        return false;
19147    };
19148    let Some(default_value) = assignment.child_by_field_name("right") else {
19149        return false;
19150    };
19151    let Some(function) = call.child_by_field_name("function") else {
19152        return false;
19153    };
19154    let Some(arguments) = call.child_by_field_name("arguments") else {
19155        return false;
19156    };
19157    if call.kind() != "call_expression"
19158        || function.kind() != "identifier"
19159        || arguments.kind() != "argument_list"
19160        || arguments.named_child_count() != 0
19161        || default_value.kind() != "identifier"
19162        || node_text(default_value, source) != "default"
19163        || node_text(function, source) != class_name
19164    {
19165        return false;
19166    }
19167    members.iter().any(|member| {
19168        if same_node(*member, node) {
19169            return false;
19170        }
19171        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
19172    })
19173}
19174
19175/// Return the constructor name from one direct class-member-shaped reparse
19176/// item. The function-declarator in an `ERROR` is admitted only through the
19177/// existing ERROR-only member proof; clean function definitions must carry no
19178/// return type and must retain either a body or ABI 15's `default_method_clause`.
19179fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
19180    let function_declarator = match node.kind() {
19181        "function_definition"
19182            if !node.has_error() && node.child_by_field_name("type").is_none() =>
19183        {
19184            let has_body = node
19185                .child_by_field_name("body")
19186                .is_some_and(|body| body.kind() == "compound_statement");
19187            let has_default_method_clause = node
19188                .named_children(&mut node.walk())
19189                .any(|child| child.kind() == "default_method_clause");
19190            if !has_body && !has_default_method_clause {
19191                return None;
19192            }
19193            node.child_by_field_name("declarator")
19194                .and_then(extract_function_declarator)
19195        }
19196        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
19197            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
19198            let mut candidate = None;
19199            while let Some(current) = stack.pop() {
19200                if current.kind() == "function_declarator" {
19201                    if candidate.is_some() {
19202                        return None;
19203                    }
19204                    candidate = Some(current);
19205                    continue;
19206                }
19207                if current.kind() == "ERROR" {
19208                    let mut cursor = current.walk();
19209                    stack.extend(current.named_children(&mut cursor));
19210                }
19211            }
19212            candidate
19213        }
19214        _ => None,
19215    }?;
19216    cpp_function_declarator_name_node(function_declarator)
19217        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
19218}
19219
19220fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
19221    cpp_reparsed_members_are_indexable_for_class(root, source, None)
19222}
19223
19224fn cpp_reparsed_members_are_indexable_for_class(
19225    root: Node<'_>,
19226    source: &str,
19227    class_name: Option<&str>,
19228) -> bool {
19229    let mut cursor = root.walk();
19230    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
19231    let mut saw_member = false;
19232    let mut index = 0;
19233    while index < children.len() {
19234        let child = children[index];
19235        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
19236            saw_member = true;
19237            index += 3;
19238            continue;
19239        }
19240        if let Some(recovered) = fragmented_class_body(child, source) {
19241            let Some(tree) = cpp_reparse_fragmented_class_body(
19242                source,
19243                recovered.body.reparse_start,
19244                recovered.body.reparse_end,
19245            ) else {
19246                return false;
19247            };
19248            if !cpp_reparsed_members_are_indexable_for_class(
19249                tree.root_node(),
19250                source,
19251                Some(recovered.name.as_str()),
19252            ) {
19253                return false;
19254            }
19255            saw_member = true;
19256            index += 1;
19257            while index < children.len()
19258                && children[index].end_byte() <= recovered.body.class_range.end_byte
19259            {
19260                index += 1;
19261            }
19262            continue;
19263        }
19264        match child.kind() {
19265            "comment" => {}
19266            "labeled_statement" => saw_member = true,
19267            "function_definition" => {
19268                if child.has_error()
19269                    && !cpp_reparsed_member_function_is_indexable(child, source)
19270                    && cpp_sentinel_macro_region(child, source).is_none()
19271                {
19272                    return false;
19273                }
19274                saw_member = true;
19275            }
19276            // The attribute a string-argument macro leaves as a call statement
19277            // of its own. It declares nothing; the member it decorated is the
19278            // sibling after it, checked on its own turn.
19279            "expression_statement" if is_string_attribute_macro_statement(child) => {}
19280            "expression_statement"
19281                if cpp_reparsed_defaulted_constructor_is_indexable(
19282                    child, &children, class_name, source,
19283                ) =>
19284            {
19285                saw_member = true;
19286            }
19287            "ERROR"
19288                if (cpp_reparsed_member_error_is_indexable(child)
19289                    || cpp_reparsed_adjacent_copy_control_error(child, source)
19290                    || cpp_reparsed_stranded_member_error(child, source))
19291                    && (child
19292                        .next_named_sibling()
19293                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
19294                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
19295            {
19296                saw_member = true;
19297            }
19298            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
19299                saw_member = true;
19300            }
19301            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
19302                saw_member = true;
19303            }
19304            "expression_statement"
19305                if cpp_is_stray_semicolon(child, source)
19306                    && child.prev_named_sibling().is_some_and(|error| {
19307                        cpp_reparsed_member_error_is_indexable(error)
19308                            || cpp_reparsed_adjacent_copy_control_error(error, source)
19309                            || cpp_reparsed_stranded_member_error(error, source)
19310                    }) =>
19311            {
19312                saw_member = true;
19313            }
19314            "compound_statement"
19315                if cpp_reparsed_constructor_body_is_indexable(child, source)
19316                    || cpp_reparsed_attribute_requires_body(child, source) =>
19317            {
19318                saw_member = true;
19319            }
19320            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
19321            _ => return false,
19322        }
19323        index += 1;
19324    }
19325    saw_member
19326}
19327
19328/// Detect the malformed constructor shape that tree-sitter exposes as an
19329/// access-label statement followed by initializer-looking declarations. The
19330/// declarations are not class members: visiting their `location(loc)` and
19331/// `string(s)` function declarators would publish synthetic functions. The
19332/// export-class fallback keeps the original sibling nodes and therefore avoids
19333/// this parser artifact. The returned range identifies the real constructor
19334/// header, which can be reparsed independently as a structured declarator.
19335fn cpp_reparsed_synthetic_initializer_constructor_range(
19336    root: Node<'_>,
19337    class_name: &str,
19338    source: &str,
19339    constructor_end: usize,
19340) -> Option<std::ops::Range<usize>> {
19341    let mut stack = {
19342        let mut cursor = root.walk();
19343        root.named_children(&mut cursor).collect::<Vec<_>>()
19344    };
19345    while let Some(current) = stack.pop() {
19346        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
19347            current,
19348            class_name,
19349            source,
19350            constructor_end,
19351        ) {
19352            return Some(range);
19353        }
19354        if current.kind() == "ERROR" {
19355            let mut cursor = current.walk();
19356            stack.extend(current.named_children(&mut cursor));
19357        }
19358    }
19359    None
19360}
19361
19362/// Recover an inline constructor that a function-like export macro makes
19363/// tree-sitter merge with the following overload. In the reparsed class-body
19364/// region, the access label wraps one declaration whose ERROR contains the
19365/// constructor declarator and its base-initializer/body, while the declaration's
19366/// ordinary declarator is the following overload. Every boundary below comes
19367/// from that CST; no source syntax is reparsed by hand.
19368fn cpp_reparsed_merged_inline_constructor<'tree>(
19369    root: Node<'tree>,
19370    class_name: &str,
19371    source: &str,
19372) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
19373    let mut stack = vec![root];
19374    while let Some(current) = stack.pop() {
19375        if current.kind() != "labeled_statement" {
19376            let mut cursor = current.walk();
19377            stack.extend(current.named_children(&mut cursor));
19378            continue;
19379        }
19380        let declaration = current
19381            .named_children(&mut current.walk())
19382            .find(|child| child.kind() == "declaration")?;
19383        if declaration
19384            .child_by_field_name("type")
19385            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
19386        {
19387            continue;
19388        }
19389        let following = declaration
19390            .child_by_field_name("declarator")
19391            .and_then(extract_function_declarator)
19392            .and_then(cpp_function_declarator_name_node);
19393        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
19394            continue;
19395        }
19396        let mut declaration_cursor = declaration.walk();
19397        let Some(error) = declaration
19398            .named_children(&mut declaration_cursor)
19399            .find(|child| child.kind() == "ERROR")
19400        else {
19401            continue;
19402        };
19403        let mut error_cursor = error.walk();
19404        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
19405        let Some(constructor) = error_children.iter().copied().find(|child| {
19406            child.kind() == "function_declarator"
19407                && cpp_function_declarator_name_node(*child)
19408                    .is_some_and(|name| node_text(name, source).trim() == class_name)
19409        }) else {
19410            continue;
19411        };
19412        let Some(body) = error_children.iter().copied().find_map(|child| {
19413            (child.kind() == "init_declarator")
19414                .then(|| child.child_by_field_name("value"))
19415                .flatten()
19416                .filter(|value| value.kind() == "initializer_list")
19417        }) else {
19418            continue;
19419        };
19420        if constructor.end_byte() > body.start_byte() {
19421            continue;
19422        }
19423        return Some((constructor.start_byte()..body.end_byte(), body));
19424    }
19425    None
19426}
19427
19428fn cpp_reparsed_synthetic_initializer_constructor(
19429    node: Node<'_>,
19430    class_name: &str,
19431    source: &str,
19432    constructor_end: usize,
19433) -> Option<std::ops::Range<usize>> {
19434    if node.kind() != "labeled_statement" {
19435        return None;
19436    }
19437    let mut cursor = node.walk();
19438    let named = node
19439        .named_children(&mut cursor)
19440        .filter(|child| child.kind() != "comment")
19441        .collect::<Vec<_>>();
19442    let label = named.first()?;
19443    if label.kind() != "statement_identifier"
19444        || !matches!(
19445            node_text(*label, source).trim(),
19446            "public" | "private" | "protected"
19447        )
19448    {
19449        return None;
19450    }
19451    let call_error_index = named.iter().position(|child| {
19452        if child.kind() != "ERROR" {
19453            return false;
19454        }
19455        let mut stack = vec![*child];
19456        while let Some(current) = stack.pop() {
19457            if current.kind() == "call_expression"
19458                && current
19459                    .child_by_field_name("function")
19460                    .is_some_and(|function| {
19461                        function.kind() == "identifier"
19462                            && node_text(function, source).trim() == class_name
19463                    })
19464            {
19465                return true;
19466            }
19467            let mut cursor = current.walk();
19468            stack.extend(current.named_children(&mut cursor));
19469        }
19470        false
19471    })?;
19472    let constructor_call = {
19473        let mut stack = vec![named[call_error_index]];
19474        let mut found = None;
19475        while let Some(current) = stack.pop() {
19476            if current.kind() == "call_expression"
19477                && current
19478                    .child_by_field_name("function")
19479                    .is_some_and(|function| {
19480                        function.kind() == "identifier"
19481                            && node_text(function, source).trim() == class_name
19482                    })
19483            {
19484                found = Some(current);
19485                break;
19486            }
19487            let mut cursor = current.walk();
19488            stack.extend(current.named_children(&mut cursor));
19489        }
19490        found
19491    };
19492    let constructor_call = constructor_call?;
19493    named.iter().skip(call_error_index + 1).find(|child| {
19494        child.kind() == "declaration" && child.has_error() && {
19495            let mut cursor = child.walk();
19496            child.named_children(&mut cursor).any(|declarator| {
19497                declarator.kind() == "init_declarator"
19498                    && declarator
19499                        .child_by_field_name("declarator")
19500                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
19501                    && declarator
19502                        .child_by_field_name("value")
19503                        .is_some_and(|value| value.kind() == "initializer_list")
19504            })
19505        }
19506    })?;
19507    Some(constructor_call.start_byte()..constructor_end)
19508}
19509
19510fn cpp_reparsed_exact_constructor_declarator<'tree>(
19511    root: Node<'tree>,
19512    start: usize,
19513    class_name: &str,
19514    source: &str,
19515) -> Option<Node<'tree>> {
19516    let mut candidate = None;
19517    let mut stack = vec![root];
19518    while let Some(current) = stack.pop() {
19519        if current.kind() == "function_declarator"
19520            && current.start_byte() == start
19521            && cpp_function_declarator_name_node(current)
19522                .is_some_and(|name| node_text(name, source).trim() == class_name)
19523        {
19524            if candidate.is_some() {
19525                return None;
19526            }
19527            candidate = Some(current);
19528            continue;
19529        }
19530        let mut cursor = current.walk();
19531        stack.extend(current.named_children(&mut cursor));
19532    }
19533    candidate
19534}
19535
19536fn cpp_is_indexable_item_kind(kind: &str) -> bool {
19537    matches!(
19538        kind,
19539        "namespace_definition"
19540            | "class_specifier"
19541            | "struct_specifier"
19542            | "union_specifier"
19543            | "enum_specifier"
19544            | "function_definition"
19545            | "template_declaration"
19546            | "declaration"
19547            | "field_declaration"
19548            | "alias_declaration"
19549            | "static_assert_declaration"
19550            | "type_definition"
19551            | "using_declaration"
19552            | "linkage_specification"
19553            | "preproc_def"
19554            | "preproc_function_def"
19555            | "preproc_include"
19556            | "preproc_if"
19557            | "preproc_ifdef"
19558            | "preproc_call"
19559    )
19560}
19561
19562#[cfg(test)]
19563mod tests {
19564    use super::*;
19565    use crate::adapter::parse_cpp_file;
19566    use brokk_bifrost_core::analyzer::parsed_file::{
19567        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
19568    };
19569    use brokk_bifrost_test_macros::hardwon;
19570    use std::fmt::Write;
19571
19572    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
19573        let tree = crate::diagnostics::parse_cpp_tree(source);
19574        let file = ProjectFile::new(std::env::temp_dir(), name);
19575        parse_cpp_file(&file, source, &tree)
19576    }
19577
19578    #[test]
19579    fn native_must_use_attribute_nodes_are_structural() {
19580        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
19581        let tree = crate::diagnostics::parse_cpp_tree(source);
19582        let root = tree.root_node();
19583        let mut declarations = Vec::new();
19584        walk_named_tree_preorder(root, true, |node| {
19585            if matches!(
19586                node.kind(),
19587                "declaration" | "function_definition" | "field_declaration"
19588            ) {
19589                declarations.push(node);
19590            }
19591            WalkControl::Continue
19592        });
19593        assert_eq!(
19594            declarations
19595                .into_iter()
19596                .map(|declaration| {
19597                    cpp_declaration_has_native_must_use_attribute(declaration, source)
19598                })
19599                .collect::<Vec<_>>(),
19600            vec![true, false, true]
19601        );
19602        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
19603        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
19604        let mut enum_specifier = None;
19605        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
19606            if node.kind() == "enum_specifier" {
19607                enum_specifier = Some(node);
19608                WalkControl::Break
19609            } else {
19610                WalkControl::Continue
19611            }
19612        });
19613        assert!(cpp_declaration_has_native_must_use_attribute(
19614            enum_specifier.expect("enum declaration"),
19615            enum_source
19616        ));
19617    }
19618
19619    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
19620        let mut rows = parsed
19621            .signature_metadata
19622            .iter()
19623            .flat_map(|(unit, entries)| {
19624                entries.iter().map(move |metadata| {
19625                    assert!(
19626                        metadata.callable_modifiers_recorded(),
19627                        "{} must record that the walk read its declaration shape",
19628                        unit.fq_name()
19629                    );
19630                    format!(
19631                        "{} decl_only={} static={} constructor={} visibility={:?}",
19632                        unit.fq_name(),
19633                        metadata.is_declaration_only(),
19634                        metadata.callable_is_static(),
19635                        metadata.callable_is_constructor(),
19636                        metadata
19637                            .callable_declared_visibility()
19638                            .expect("a recorded modifier set states a visibility"),
19639                    )
19640                })
19641            })
19642            .collect::<Vec<_>>();
19643        rows.sort();
19644        rows
19645    }
19646
19647    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
19648    /// exactly the spelling it was given, with the nested-class `$` recorded as
19649    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
19650    #[test]
19651    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
19652    fn rendered_name_fq_round_trips_nested_class_spellings() {
19653        assert!(cpp_rendered_name_fq("").is_empty());
19654        for rendered in [
19655            "Only",
19656            "Top$Only",
19657            "ns.Outer$Inner",
19658            "Top$Only.b",
19659            "ns.Outer$Inner.a",
19660            "Buck$Stop",
19661        ] {
19662            let fq = cpp_rendered_name_fq(rendered);
19663            assert_eq!(
19664                fq.display_native(Language::Cpp, segment_interner()),
19665                rendered,
19666                "native round trip for {rendered}"
19667            );
19668        }
19669        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
19670            .segments()
19671            .iter()
19672            .map(|segment| segment_interner().resolve(*segment).1)
19673            .collect();
19674        assert_eq!(
19675            kinds,
19676            [
19677                SegmentKind::Unknown,
19678                SegmentKind::Unknown,
19679                SegmentKind::Nested
19680            ]
19681        );
19682    }
19683
19684    /// #3631: a `::`-only source spelling must name the nested `$` identity
19685    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
19686    /// the single rendered-name parse.
19687    #[test]
19688    #[hardwon(
19689        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
19690    )]
19691    fn colon_source_aliases_name_nested_and_namespace_identities() {
19692        let native = |rendered: &str| {
19693            cpp_colon_source_aliases(rendered)
19694                .into_iter()
19695                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
19696                .collect::<Vec<_>>()
19697        };
19698        let top = native("Top::Only");
19699        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
19700        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
19701        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
19702        let nested = native("ns::Outer::Inner");
19703        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
19704        assert!(
19705            nested.contains(&"ns::Outer.Inner".to_string()),
19706            "{nested:?}"
19707        );
19708        let member = native("Top::Only::b");
19709        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
19710        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
19711        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
19712        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
19713        assert!(cpp_colon_source_aliases("Only").is_empty());
19714        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
19715            fq.segments()
19716                .iter()
19717                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
19718        }));
19719    }
19720
19721    #[test]
19722    #[hardwon(
19723        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
19724    )]
19725    fn displaced_terminator_ignores_nested_initializer_endif() {
19726        let source = "#ifdef ENABLE_ITEMS\n\
19727struct Item { int reg; };\n\
19728static const struct Item items[] = {{1}};\n\
19729static const struct Item extras[] = {\n\
19730  {0},\n\
19731#ifndef REDUCED\n\
19732  {1},\n\
19733#endif\n\
19734};\n\
19735int read_item(int i) { return items[i].reg; }\n\
19736#endif\n";
19737        let mut parser = tree_sitter::Parser::new();
19738        parser
19739            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19740            .expect("C++ grammar");
19741        let tree = parser.parse(source, None).expect("fixture tree");
19742        let conditional = tree
19743            .root_node()
19744            .named_child(0)
19745            .filter(|node| node.kind() == "preproc_ifdef")
19746            .expect("outer conditional");
19747
19748        assert!(conditional.has_error());
19749        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
19750        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
19751    }
19752
19753    #[test]
19754    #[hardwon(
19755        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
19756    )]
19757    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
19758        let source = "struct Image { PyObject_HEAD Imaging image; };";
19759        let parsed = parse_cpp_declarations(source, "image.h");
19760        let names = parsed
19761            .declarations()
19762            .iter()
19763            .map(|unit| unit.fq_name())
19764            .collect::<Vec<_>>();
19765
19766        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
19767        assert!(
19768            names.iter().all(|name| name != "Image.Imaging"),
19769            "the pseudo-declarator must not become a field: {names:#?}"
19770        );
19771
19772        let pointer = parse_cpp_declarations(
19773            "struct Image { PyObject_HEAD Imaging *image; };",
19774            "image-pointer.h",
19775        );
19776        let pointer_names = pointer
19777            .declarations()
19778            .iter()
19779            .map(|unit| unit.fq_name())
19780            .collect::<Vec<_>>();
19781        assert!(
19782            pointer_names.iter().any(|name| name == "Image.image"),
19783            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
19784        );
19785        assert!(
19786            pointer_names.iter().all(|name| name != "Image.Imaging"),
19787            "the pointer recovery error must not become a field: {pointer_names:#?}"
19788        );
19789
19790        let unrelated_macro = parse_cpp_declarations(
19791            "struct Image { OTHER_HEAD Imaging other; };",
19792            "image-near-miss.h",
19793        );
19794        let unrelated_names = unrelated_macro
19795            .declarations()
19796            .iter()
19797            .map(|unit| unit.fq_name())
19798            .collect::<Vec<_>>();
19799        assert!(
19800            unrelated_names.iter().all(|name| name != "Image.other"),
19801            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
19802        );
19803    }
19804
19805    #[test]
19806    #[hardwon(
19807        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
19808    )]
19809    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
19810        let source = r#"namespace testing {
19811namespace internal {
19812    namespace detail {
19813        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
19814        public:
19815            int value() const { return count_ + 1; }
19816        private:
19817            int count_;
19818        };
19819        class GTEST_API_ [[nodiscard]] OtherReporter {
19820        public:
19821            int value() const { return count_ + 2; }
19822        private:
19823            int count_;
19824        };
19825    }
19826
19827    template <typename T>
19828    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
19829
19830    class TailReporter {};
19831}
19832}
19833"#;
19834        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
19835        let declarations = parsed.declarations();
19836        let mut parser = tree_sitter::Parser::new();
19837        parser
19838            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19839            .unwrap();
19840        let tree = parser.parse(source, None).unwrap();
19841        let tail_start = source.find("TailReporter").expect("tail class");
19842        let tail_node = tree
19843            .root_node()
19844            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
19845            .expect("tail class AST node");
19846        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
19847        assert!(
19848            tree.root_node().has_error(),
19849            "the malformed class must exercise recovery"
19850        );
19851        assert!(index.region_at(tail_start).is_some());
19852        assert_eq!(
19853            index.enclosing_namespace_components(tail_node, source),
19854            ["testing", "internal"]
19855        );
19856        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
19857        let mut recovered_parsed = ParsedFile::new(String::new());
19858        let class_unit = CodeUnit::new_fq(
19859            file.clone(),
19860            CodeUnitType::Class,
19861            "testing",
19862            "ScopedFakeTestPartResultReporter",
19863            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
19864        );
19865        let scope = ScopeInfo {
19866            package_name: "testing".to_string(),
19867            module: None,
19868            class_unit: Some(class_unit),
19869            template_signature: Some("<typename T>".to_string()),
19870            template_metadata: Some(CppTemplateMetadata {
19871                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
19872                primary_fq_name: String::new(),
19873                parameters: Vec::new(),
19874                specialization_arguments: Vec::new(),
19875                alias_target: None,
19876            }),
19877            declarations_are_fields: true,
19878            recovered_specialization_member_scope: true,
19879            visible_using_namespaces: Vec::new(),
19880        };
19881        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
19882        let properties = RefCell::new(HashMap::default());
19883        let dependencies =
19884            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
19885        let ancestry = ParentIndex::new(tree.root_node());
19886        let mut visitor = CppVisitor {
19887            file: &file,
19888            source,
19889            parsed: &mut recovered_parsed,
19890            source_collector: &collector,
19891            source_properties: &properties,
19892            source_dependencies: &dependencies,
19893            primary_ancestry: &ancestry,
19894            primary_root_id: tree.root_node().id(),
19895            source_links: HashMap::default(),
19896            source_declarations: HashSet::default(),
19897            preprocessor_context_cache: Default::default(),
19898            active_source_context: None,
19899            c_tag_semantics: false,
19900            c_tag_scope_witness: false,
19901            recovered_class_sibling_scopes: HashMap::default(),
19902            consumed_fragment_regions: Vec::new(),
19903            orphaned_namespaces: &index,
19904            partitioned_regions: Vec::new(),
19905            namespace_forward_scans: HashMap::default(),
19906            field_owners: None,
19907            recovery_captures: Vec::new(),
19908            object_macro_fields: HashMap::default(),
19909            ambiguous_object_macro_fields: HashSet::default(),
19910        };
19911        let recovered = visitor
19912            .recovered_namespace_scope(tail_node, &scope)
19913            .expect("the tail must use the stolen namespace scope");
19914        assert_eq!(recovered.package_name, "testing::internal");
19915        assert!(
19916            recovered.class_unit.is_none(),
19917            "recovered namespace declarations cannot retain the malformed class owner"
19918        );
19919        assert!(recovered.template_signature.is_none());
19920        assert!(recovered.template_metadata.is_none());
19921        assert!(!recovered.declarations_are_fields);
19922        assert!(!recovered.recovered_specialization_member_scope);
19923        assert!(
19924            declarations
19925                .iter()
19926                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
19927            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
19928        );
19929        assert!(
19930            declarations
19931                .iter()
19932                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
19933            "the recovered free function remains in its namespace: {declarations:#?}"
19934        );
19935        assert!(
19936            declarations
19937                .iter()
19938                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
19939            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
19940        );
19941        assert!(
19942            declarations.iter().all(|unit| {
19943                !unit
19944                    .short_name()
19945                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
19946            }),
19947            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
19948        );
19949        assert!(
19950            declarations
19951                .iter()
19952                .all(|unit| !unit.identifier().is_empty()),
19953            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
19954        );
19955    }
19956
19957    /// #4292: a recovered namespace level whose close the brace stack could not
19958    /// prove keeps its head-only range, and the first declaration the walk
19959    /// reaches inside the region is not the namespace. The Module's name
19960    /// occurrence must be the level's own name token; deriving it from the
19961    /// walk's current node paired the head range with an unrelated name and
19962    /// panicked the collector's containment assertion.
19963    #[test]
19964    fn recovered_namespace_levels_name_their_own_name_tokens() {
19965        // #3084's collapsed-head fixture without the namespace's own close:
19966        // the file ends inside the namespace, so the brace stack proves no
19967        // close for its `{` and the level keeps its `namespace Name {` head
19968        // alone (#3328).
19969        let source = r#"namespace outer::app {
19970
19971    class Target {
19972        int value_;
19973    };
19974
19975    template <typename T>
19976    class Holder {
19977    public:
19978        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
19979
19980#define HOLDER_DEFINE_OP( id, op )                                             \
19981    template <typename U>                                                      \
19982    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
19983        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
19984        return Target{};                                                       \
19985    }
19986
19987        HOLDER_DEFINE_OP( equal, == )
19988#undef HOLDER_DEFINE_OP
19989        T m_lhs;
19990    };
19991
19992    class Tail {};
19993"#;
19994        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
19995        let mut parser = tree_sitter::Parser::new();
19996        parser
19997            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19998            .unwrap();
19999        let tree = parser.parse(source, None).unwrap();
20000        assert!(
20001            tree.root_node().has_error(),
20002            "the fixture must reproduce the parse damage"
20003        );
20004        let target_start = source.find("class Target").expect("target class");
20005        let target_node = tree
20006            .root_node()
20007            .named_descendant_for_byte_range(target_start, target_start + "class".len())
20008            .expect("target class AST node");
20009        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
20010        assert!(
20011            index.region_at(target_start).is_some(),
20012            "the collapsed namespaces must restore a region over the members"
20013        );
20014        assert_eq!(
20015            index.enclosing_namespace_components(target_node, source),
20016            ["outer", "app"]
20017        );
20018        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
20019        let mut recovered_parsed = ParsedFile::new(String::new());
20020        let scope = ScopeInfo {
20021            package_name: String::new(),
20022            module: None,
20023            class_unit: None,
20024            template_signature: None,
20025            template_metadata: None,
20026            declarations_are_fields: false,
20027            recovered_specialization_member_scope: false,
20028            visible_using_namespaces: Vec::new(),
20029        };
20030        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
20031        let properties = RefCell::new(HashMap::default());
20032        let dependencies =
20033            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
20034        let ancestry = ParentIndex::new(tree.root_node());
20035        let mut visitor = CppVisitor {
20036            file: &file,
20037            source,
20038            parsed: &mut recovered_parsed,
20039            source_collector: &collector,
20040            source_properties: &properties,
20041            source_dependencies: &dependencies,
20042            primary_ancestry: &ancestry,
20043            primary_root_id: tree.root_node().id(),
20044            source_links: HashMap::default(),
20045            source_declarations: HashSet::default(),
20046            preprocessor_context_cache: Default::default(),
20047            active_source_context: None,
20048            c_tag_semantics: false,
20049            c_tag_scope_witness: false,
20050            recovered_class_sibling_scopes: HashMap::default(),
20051            consumed_fragment_regions: Vec::new(),
20052            orphaned_namespaces: &index,
20053            partitioned_regions: Vec::new(),
20054            namespace_forward_scans: HashMap::default(),
20055            field_owners: None,
20056            recovery_captures: Vec::new(),
20057            object_macro_fields: HashMap::default(),
20058            ambiguous_object_macro_fields: HashSet::default(),
20059        };
20060        let recovered = visitor
20061            .recovered_namespace_scope(target_node, &scope)
20062            .expect("the members must use the recovered namespace scope");
20063        assert_eq!(recovered.package_name, "outer::app");
20064        let collector = collector.borrow();
20065        for (module_name, expected_name) in [
20066            (
20067                "outer",
20068                Range {
20069                    start_byte: 10,
20070                    end_byte: 15,
20071                    start_line: 1,
20072                    end_line: 1,
20073                },
20074            ),
20075            (
20076                "outer::app",
20077                Range {
20078                    start_byte: 17,
20079                    end_byte: 20,
20080                    start_line: 1,
20081                    end_line: 1,
20082                },
20083            ),
20084        ] {
20085            let declaration = visitor
20086                .source_links
20087                .iter()
20088                .find(|(unit, _)| unit.fq_name() == module_name)
20089                .expect("the recovered module links a source declaration")
20090                .1
20091                .last()
20092                .expect("the recovered module declaration");
20093            let declaration = collector.declaration(*declaration);
20094            let name = declaration
20095                .name
20096                .expect("the recovered module records its name occurrence");
20097            assert_eq!(
20098                collector.occurrence(name).range,
20099                expected_name,
20100                "the recovered module names its own token: {module_name}"
20101            );
20102        }
20103        assert!(
20104            parsed.declarations().iter().any(|unit| {
20105                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
20106            }),
20107            "the recovered namespace members keep their namespace: {:#?}",
20108            parsed.declarations()
20109        );
20110    }
20111
20112    #[test]
20113    #[hardwon(
20114        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
20115    )]
20116    fn object_like_field_macros_materialize_owner_specific_declarations() {
20117        let source = r#"#define PUBLIC_FIELDS int public_value;
20118#define PRIVATE_FIELDS int private_value;
20119#define NOT_A_FIELD_LIST not a declaration
20120
20121struct First {
20122  PUBLIC_FIELDS
20123  PRIVATE_FIELDS
20124};
20125struct Second {
20126  PUBLIC_FIELDS
20127  NOT_A_FIELD_LIST
20128};
20129#undef PUBLIC_FIELDS
20130struct Third {
20131  PUBLIC_FIELDS
20132};
20133"#;
20134        let parsed = parse_cpp_declarations(source, "macro-fields.c");
20135        let fields = parsed
20136            .declarations()
20137            .iter()
20138            .filter(|unit| unit.is_field())
20139            .map(|unit| unit.fq_name())
20140            .collect::<Vec<_>>();
20141
20142        assert!(
20143            fields.contains(&"First.public_value".to_string()),
20144            "{fields:?}"
20145        );
20146        assert!(
20147            fields.contains(&"First.private_value".to_string()),
20148            "{fields:?}"
20149        );
20150        assert!(
20151            fields.contains(&"Second.public_value".to_string()),
20152            "{fields:?}"
20153        );
20154        assert!(
20155            !fields.iter().any(|field| field.contains("not_a_field")),
20156            "malformed macro must fail closed: {fields:?}"
20157        );
20158        assert!(
20159            !fields.iter().any(|field| field.starts_with("Third.")),
20160            "undefined macro must fail closed: {fields:?}"
20161        );
20162    }
20163
20164    #[test]
20165    fn conditional_macro_redefinition_is_ambiguous() {
20166        let source = "#define FIELDS int first;\n#if USE_FIRST\n#else\n#undef FIELDS\n#define FIELDS int second;\n#endif\nstruct Item { FIELDS };\n";
20167        let tree = crate::diagnostics::parse_cpp_tree(source);
20168        let fields = collect_cpp_object_macro_fields(tree.root_node(), source);
20169        assert!(
20170            !fields.contains_key("FIELDS"),
20171            "conditional macro state must not select one branch's declaration: {fields:?}"
20172        );
20173    }
20174
20175    #[test]
20176    #[hardwon(
20177        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
20178    )]
20179    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
20180        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
20181        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
20182        let mut macros = parsed
20183            .declarations()
20184            .iter()
20185            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
20186            .collect::<Vec<_>>();
20187        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
20188
20189        assert_eq!(macros.len(), 2, "{macros:#?}");
20190        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
20191        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
20192        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
20193        assert_eq!(
20194            parsed.declaration_ranges(macros[1])[0].start_byte,
20195            source.rfind("#define VALUE 2").expect("second definition")
20196        );
20197    }
20198
20199    #[test]
20200    #[hardwon(
20201        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
20202    )]
20203    fn identifies_export_macro_class_base_displaced_into_declarator() {
20204        let source = r#"#define PROJECT_API_
20205namespace project {
20206namespace internal {
20207template <typename T>
20208class Base {};
20209}
20210template <typename T>
20211class Wrapper;
20212template <>
20213class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
20214}
20215"#;
20216        let mut parser = tree_sitter::Parser::new();
20217        parser
20218            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20219            .unwrap();
20220        let tree = parser.parse(source, None).unwrap();
20221        let start = source.find("internal::Base<int>").expect("base");
20222        let mut base = tree
20223            .root_node()
20224            .descendant_for_byte_range(start, start + 8)
20225            .expect("base syntax");
20226        while base.kind() != "qualified_identifier" {
20227            base = base.parent().expect("qualified base ancestor");
20228        }
20229        assert!(
20230            is_recovered_exported_class_base_type_node(base, source),
20231            "{}",
20232            tree.root_node().to_sexp()
20233        );
20234    }
20235
20236    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
20237        parsed
20238            .declarations()
20239            .iter()
20240            .filter(|unit| unit.is_function())
20241            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
20242            .collect()
20243    }
20244
20245    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
20246        let mut identities = all_function_identities(parsed)
20247            .into_iter()
20248            .map(|(name, signature)| (name, signature.unwrap_or_default()))
20249            .collect::<Vec<_>>();
20250        identities.sort();
20251        identities
20252    }
20253
20254    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
20255    /// prototype macros. Each case is parsed independently so a preceding
20256    /// malformed line cannot flatten the next declaration's fields into one
20257    /// cascading `ERROR`; that shape has no structural proof and must fail
20258    /// closed rather than fall back to a token scan. Parameter names are
20259    /// dropped from signatures just as they are for ordinary C prototypes.
20260    #[test]
20261    #[hardwon(
20262        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
20263    )]
20264    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
20265        let cases = [
20266            (
20267                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
20268                "pg_typemap_fit_to_result",
20269                "(VALUE, VALUE)",
20270            ),
20271            (
20272                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
20273                "pg_typemap_result_value",
20274                "(t_typemap *, VALUE, int, int)",
20275            ),
20276            (
20277                "void pg_typemap_mark _(( void * ));",
20278                "pg_typemap_mark",
20279                "(void *)",
20280            ),
20281            (
20282                "void init_pg_type_map _(( void ));",
20283                "init_pg_type_map",
20284                "(void)",
20285            ),
20286            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
20287            (
20288                "extern VALUE pg_extern _(( VALUE, VALUE ));",
20289                "pg_extern",
20290                "(VALUE, VALUE)",
20291            ),
20292            (
20293                "size_t pg_typemap_memsize _(( const void * ));",
20294                "pg_typemap_memsize",
20295                "(const void *)",
20296            ),
20297            (
20298                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
20299                "pg_wrap_socket_io",
20300                "(int, VALUE, VALUE *, int *)",
20301            ),
20302            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
20303            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
20304            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
20305        ];
20306
20307        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
20308            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
20309            assert_eq!(
20310                function_identities(&parsed),
20311                vec![(name.to_string(), signature.to_string())],
20312                "{source}: {:#?}",
20313                parsed.declarations()
20314            );
20315            assert!(
20316                parsed.declarations().iter().all(|unit| !unit.is_field()),
20317                "{source} must not retain the malformed field: {:#?}",
20318                parsed.declarations()
20319            );
20320        }
20321    }
20322
20323    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
20324    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
20325    /// it as a multiplication of a type by a call
20326    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
20327    /// wrapped in an `expression_statement` that `has_error()` only because
20328    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
20329    /// here with a clean preceding field so the prototype remains the
20330    /// parser-owned `expression_statement` this recovery requires.
20331    #[test]
20332    #[hardwon(
20333        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
20334    )]
20335    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
20336        let cases = [
20337            (
20338                "PGconn *pg_get_pgconn _(( VALUE ));",
20339                "pg_get_pgconn",
20340                "(VALUE)",
20341            ),
20342            (
20343                "PGresult* pgresult_get _(( VALUE ));",
20344                "pgresult_get",
20345                "(VALUE)",
20346            ),
20347        ];
20348        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
20349            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
20350            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
20351            assert_eq!(
20352                function_identities(&parsed),
20353                vec![(name.to_string(), signature.to_string())],
20354                "{source}: {:#?}",
20355                parsed.declarations()
20356            );
20357            assert_eq!(
20358                parsed
20359                    .declarations()
20360                    .iter()
20361                    .filter(|unit| unit.is_field())
20362                    .map(|unit| unit.fq_name())
20363                    .collect::<Vec<_>>(),
20364                vec!["rb_mPG".to_string()],
20365                "{source}: {:#?}",
20366                parsed.declarations()
20367            );
20368        }
20369    }
20370
20371    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
20372    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
20373    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
20374    /// regions. The witnessed declaration must still become a Function and
20375    /// its type must not survive as the name of a bogus Field. This test does
20376    /// not claim that structurally flattened neighboring prototypes are safe
20377    /// to recover.
20378    #[test]
20379    #[hardwon(
20380        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
20381    )]
20382    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
20383        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
20384VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
20385int pg_typemap_fit_to_copy_get                         _(( VALUE ));
20386VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
20387t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
20388VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
20389void pg_typemap_mark                                   _(( void * ));
20390size_t pg_typemap_memsize                              _(( const void * ));
20391void pg_typemap_compact                                _(( void * ));
20392
20393PGconn *pg_get_pgconn                                  _(( VALUE ));
20394t_pg_connection *pg_get_connection                     _(( VALUE ));
20395VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
20396#ifdef __GNUC__
20397__attribute__((format(printf, 3, 4)))
20398#endif
20399NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
20400VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
20401void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
20402
20403
20404VALUE pg_new_result                                    _(( PGresult *, VALUE ));
20405VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
20406PGresult* pgresult_get                                 _(( VALUE ));
20407VALUE pg_result_check                                  _(( VALUE ));
20408VALUE pg_result_clear                                  _(( VALUE ));
20409VALUE pg_tuple_new                                     _(( VALUE, int ));
20410
20411/*
20412 * Fetch the data pointer for the result object
20413 */
20414static inline t_pg_result *
20415pgresult_get_this( VALUE self )
20416{
20417	return RTYPEDDATA_DATA(self);
20418}
20419
20420
20421rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
20422const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
20423rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
20424
20425"#;
20426        let parsed = parse_cpp_declarations(source, "pg.h");
20427        assert!(
20428            function_identities(&parsed)
20429                .iter()
20430                .any(|(name, signature)| name == "pg_typemap_result_value"
20431                    && signature == "(t_typemap *, VALUE, int, int)"),
20432            "the real issue witness must be a Function with its C signature: {:#?}",
20433            parsed.declarations()
20434        );
20435        assert!(
20436            parsed
20437                .declarations()
20438                .iter()
20439                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
20440            "the issue witness must not leave its return type as a Field name: {:#?}",
20441            parsed.declarations()
20442        );
20443    }
20444
20445    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
20446    /// between `explicit` and a constructor name onto the name, producing a
20447    /// `qualified_identifier` whose `::` it had to invent. The declared member
20448    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
20449    /// macro in type position, was already recovered and is the control that
20450    /// both spellings agree.
20451    #[test]
20452    #[hardwon(
20453        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
20454    )]
20455    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
20456        let source = r#"class SIMD_4x26 final {
20457   public:
20458      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
20459      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
20460      int m_v;
20461};
20462"#;
20463        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
20464        assert_eq!(
20465            function_identities(&parsed),
20466            vec![
20467                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
20468                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
20469            ],
20470            "{:#?}",
20471            parsed.declarations()
20472        );
20473    }
20474
20475    #[test]
20476    #[hardwon(
20477        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
20478    )]
20479    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
20480        let source = r#"struct cpu_spec {};
20481static struct cpu_spec __initdata cpu_specs[];
20482static struct cpu_spec ns::cpu_specs[];
20483"#;
20484        let tree = crate::diagnostics::parse_cpp_tree(source);
20485        let root = tree.root_node();
20486        let declarations = root
20487            .named_children(&mut root.walk())
20488            .filter(|node| node.kind() == "declaration")
20489            .collect::<Vec<_>>();
20490        let names = declarations
20491            .iter()
20492            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
20493            .filter_map(|declarator| extract_variable_name(declarator, source))
20494            .collect::<Vec<_>>();
20495        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
20496    }
20497
20498    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
20499    /// `ERROR` holding the wrapped declaration and every declaration after it
20500    /// until the parser recovers. All of them must be indexed, each with the
20501    /// byte range that spells it.
20502    #[test]
20503    #[hardwon(
20504        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
20505    )]
20506    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
20507        let source = r#"#include <cstdint>
20508struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
20509    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
20510                     struct llama_model * model,
20511              struct llama_context_params   params),
20512            "use llama_init_from_model instead");
20513    LLAMA_API int32_t llama_tokenize(
20514        const struct llama_vocab * vocab,
20515                      const char * text,
20516                            bool   parse_special);
20517    LLAMA_API int32_t llama_other(int a);
20518"#;
20519        let parsed = parse_cpp_declarations(source, "llama.h");
20520        assert_eq!(
20521            function_identities(&parsed),
20522            vec![
20523                (
20524                    "llama_new_context_with_model".to_string(),
20525                    "(struct llama_model *, struct llama_context_params)".to_string()
20526                ),
20527                ("llama_other".to_string(), "(int)".to_string()),
20528                (
20529                    "llama_tokenize".to_string(),
20530                    "(const struct llama_vocab *, const char *, bool)".to_string()
20531                ),
20532            ],
20533            "{:#?}",
20534            parsed.declarations()
20535        );
20536
20537        // Each recovered declaration owns the source that spells it, so
20538        // navigation lands on the declaration and not on the macro envelope.
20539        for (name, expected) in [
20540            (
20541                "llama_new_context_with_model",
20542                "LLAMA_API struct llama_context * llama_new_context_with_model(",
20543            ),
20544            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
20545            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
20546        ] {
20547            let unit = parsed
20548                .declarations()
20549                .iter()
20550                .find(|unit| unit.is_function() && unit.fq_name() == name)
20551                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
20552            let [range] = parsed.declaration_ranges(unit) else {
20553                panic!("{name} must have exactly one range");
20554            };
20555            let text = &source[range.start_byte..range.end_byte];
20556            assert!(
20557                text.starts_with(expected),
20558                "{name} range is {text:?}, expected it to start with {expected:?}"
20559            );
20560            assert!(
20561                text.ends_with(')') || text.ends_with(';'),
20562                "{name}: {text:?}"
20563            );
20564        }
20565    }
20566
20567    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
20568    /// invocation's `(` at the end of its line, and the parser then keeps no
20569    /// invocation whole. It hands the first one's hint and its own `)` and `;`
20570    /// to the `expression_statement` beside it, and flattens the ones after
20571    /// that into bare `identifier`/`type_identifier`, `(`,
20572    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
20573    /// already inside. No part of that is a head the grouping can be read from,
20574    /// which is why the invocation is read from the token order instead
20575    /// (#3094).
20576    ///
20577    /// The identifiers are `library_*` at whisper's own lengths on purpose:
20578    /// tree-sitter prices error recovery partly by how much input a repair
20579    /// skips, and the same construct written with shorter names parses cleanly.
20580    #[test]
20581    #[hardwon(
20582        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
20583    )]
20584    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
20585        let source = r#"    LIBRARY_DEPRECATED(
20586        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
20587        "use library_init_from_file_with_params instead"
20588    );
20589    LIBRARY_DEPRECATED(
20590        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
20591        "use library_init_from_buffer_with_params instead"
20592    );
20593    LIBRARY_DEPRECATED(
20594        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
20595        "use library_init_with_params instead"
20596    );
20597"#;
20598        let mut parser = tree_sitter::Parser::new();
20599        parser
20600            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20601            .expect("C++ grammar");
20602        let tree = parser.parse(source, None).expect("C++ tree");
20603        let root = tree.root_node();
20604
20605        // The shape this recovery exists for: the second invocation is not a
20606        // node of its own, and the third is a run of bare siblings inside the
20607        // second one's `ERROR`.
20608        let mut cursor = root.walk();
20609        let items = root.children(&mut cursor).collect::<Vec<_>>();
20610        let [first, hint_statement, rest @ ..] = items.as_slice() else {
20611            panic!("{}", root.to_sexp());
20612        };
20613        assert_eq!(first.kind(), "ERROR");
20614        assert_eq!(hint_statement.kind(), "expression_statement");
20615        assert!(
20616            node_text(*first, source).ends_with(','),
20617            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
20618            root.to_sexp()
20619        );
20620        let swallowing = rest
20621            .iter()
20622            .find(|item| item.kind() == "ERROR")
20623            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
20624        let mut swallowing_cursor = swallowing.walk();
20625        let flattened = swallowing
20626            .children(&mut swallowing_cursor)
20627            .map(|child| child.kind())
20628            .collect::<Vec<_>>();
20629        assert_eq!(
20630            flattened,
20631            vec![
20632                "identifier",
20633                "(",
20634                "parameter_declaration",
20635                ",",
20636                "ERROR",
20637                "type_identifier",
20638                "(",
20639                "parameter_declaration",
20640                ",",
20641                "\"",
20642                "identifier",
20643                "identifier",
20644                "identifier",
20645                "\"",
20646                ")",
20647            ],
20648            "the third invocation must be flattened into the second one's node: {}",
20649            root.to_sexp()
20650        );
20651
20652        // Both are admitted, and each names the byte just past its own `;`.
20653        let ancestry = ParentIndex::new(root);
20654        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
20655            .expect("the first invocation");
20656        assert_eq!(
20657            &source[..first_run.invocation_end],
20658            &source[..source.find("instead\"\n    );").expect("first hint")
20659                + "instead\"\n    );".len()]
20660        );
20661        assert_eq!(
20662            first_run.region_end, first_run.invocation_end,
20663            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
20664        );
20665        let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
20666            .expect("the second invocation");
20667        assert!(
20668            swallowing_run.invocation_end < swallowing.end_byte(),
20669            "the second invocation swallowed the third"
20670        );
20671        assert_eq!(
20672            swallowing_run.region_end,
20673            root.end_byte(),
20674            "a swallowing invocation owns the region to the close of its declaration scope"
20675        );
20676
20677        let parsed = parse_cpp_declarations(source, "library.h");
20678        assert_eq!(
20679            function_identities(&parsed),
20680            vec![
20681                (
20682                    "library_init".to_string(),
20683                    "(struct library_model_loader *)".to_string()
20684                ),
20685                (
20686                    "library_init_from_buffer".to_string(),
20687                    "(void *, size_t)".to_string()
20688                ),
20689                (
20690                    "library_init_from_file".to_string(),
20691                    "(const char *)".to_string()
20692                ),
20693            ],
20694            "{:#?}",
20695            parsed.declarations()
20696        );
20697    }
20698
20699    #[test]
20700    #[hardwon(
20701        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
20702    )]
20703    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
20704        let source = r#"void host() {
20705#ifdef ENABLED
20706LIBRARY_DEPRECATED(
20707    int hidden(const char* value),
20708    "hint"
20709);
20710#endif
20711}
20712"#;
20713        let mut parser = tree_sitter::Parser::new();
20714        parser
20715            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20716            .expect("C++ grammar");
20717        let tree = parser.parse(source, None).expect("C++ tree");
20718        let root = tree.root_node();
20719        let function = root.named_child(0).expect("ordinary function");
20720        let body = function
20721            .child_by_field_name("body")
20722            .expect("ordinary function body");
20723        let conditional = body.named_child(0).expect("conditional body");
20724        let mut conditional_cursor = conditional.walk();
20725        let envelope = conditional
20726            .named_children(&mut conditional_cursor)
20727            .find(|child| child.kind() == "ERROR")
20728            .expect("macro error");
20729        assert_eq!(conditional.kind(), "preproc_ifdef");
20730        assert_eq!(envelope.kind(), "ERROR");
20731
20732        let ancestry = ParentIndex::new(root);
20733        assert!(declaration_scope_container(envelope, &ancestry).is_none());
20734        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
20735
20736        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
20737        assert!(
20738            parsed
20739                .declarations()
20740                .iter()
20741                .all(|unit| unit.fq_name() != "hidden"),
20742            "a function-body macro error must not become a global declaration: {:#?}",
20743            parsed.declarations()
20744        );
20745    }
20746
20747    /// The hand-off that keeps the scan finite. An invocation that reaches the
20748    /// end of its own declaration scope swallowed nothing, and the ordinary
20749    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
20750    /// that region for every invocation it finds, so admitting it here would
20751    /// hand the scan its own input back.
20752    #[test]
20753    #[hardwon(
20754        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
20755    )]
20756    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
20757        let source = r#"LIBRARY_DEPRECATED(
20758        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
20759        "use library_init_from_file_with_params instead"
20760    );"#;
20761        let mut parser = tree_sitter::Parser::new();
20762        parser
20763            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20764            .expect("C++ grammar");
20765        let tree = parser.parse(source, None).expect("C++ tree");
20766        let root = tree.root_node();
20767        let head = root.named_child(0).expect("the invocation");
20768        let ancestry = ParentIndex::new(root);
20769        assert!(
20770            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
20771            "{}",
20772            root.to_sexp()
20773        );
20774        assert!(
20775            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
20776            "the ordinary reader must be the one that has it: {}",
20777            root.to_sexp()
20778        );
20779        // The index is a speed substitution, never a semantic one: the
20780        // unindexed path is `Node::parent` itself and must agree.
20781        let unindexed = ParentIndex::unindexed();
20782        assert!(
20783            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
20784            "indexed and unindexed climbs must agree"
20785        );
20786        assert_eq!(
20787            macro_wrapped_declarations(head, source, &ancestry).len(),
20788            macro_wrapped_declarations(head, source, &unindexed).len(),
20789            "indexed and unindexed climbs must agree"
20790        );
20791    }
20792
20793    /// Negative controls for the same recovery: a macro invocation whose
20794    /// arguments are not a declaration recovers nothing, whether the parser
20795    /// keeps it clean, reads the arguments as a type, or reads them as a bare
20796    /// declarator.
20797    #[test]
20798    #[hardwon(
20799        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
20800    )]
20801    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
20802        for source in [
20803            "int before;\nFOO(1, 2);\nint after;\n",
20804            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
20805            "int before;\nMACRO(int a, int b);\nint after;\n",
20806            "DECLARE_HANDLE(HWND);\nint after;\n",
20807        ] {
20808            let parsed = parse_cpp_declarations(source, "macro-call.h");
20809            assert_eq!(
20810                function_identities(&parsed),
20811                Vec::new(),
20812                "{source:?} must declare no function: {:#?}",
20813                parsed.declarations()
20814            );
20815        }
20816    }
20817
20818    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
20819    /// makes tree-sitter read the macro as the member's type and its argument
20820    /// list as a parenthesized declarator, which then swallows the attributed
20821    /// member and the member written after it. Both are members.
20822    #[test]
20823    #[hardwon(
20824        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
20825    )]
20826    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
20827        let source = r#"#include <string_view>
20828namespace Botan {
20829class DL_Group final {
20830   public:
20831      DL_Group() = default;
20832      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
20833      DL_Group(std::string_view pem, int format);
20834      size_t get_p() const;
20835};
20836}
20837"#;
20838        let parsed = parse_cpp_declarations(source, "dl_group.h");
20839        assert_eq!(
20840            function_identities(&parsed),
20841            vec![
20842                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
20843                (
20844                    "Botan.DL_Group.DL_Group".to_string(),
20845                    "(std::string_view)".to_string()
20846                ),
20847                (
20848                    "Botan.DL_Group.DL_Group".to_string(),
20849                    "(std::string_view, int)".to_string()
20850                ),
20851                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
20852            ],
20853            "{:#?}",
20854            parsed.declarations()
20855        );
20856    }
20857
20858    /// #2552 shape 3, export-macro class. The class head macro makes the body a
20859    /// `compound_statement`, so members come from the region reparse, and one
20860    /// string-attribute member used to make that reparse unindexable -- which
20861    /// left the class with no members at all.
20862    #[test]
20863    #[hardwon(
20864        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
20865    )]
20866    fn an_export_macro_class_keeps_its_string_attribute_members() {
20867        let source = r#"#include <string_view>
20868namespace Botan {
20869class BOTAN_PUBLIC_API(2, 0) DL_Group final {
20870   public:
20871      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
20872      DL_Group(std::string_view pem, int format);
20873      size_t get_p() const;
20874};
20875}
20876"#;
20877        let parsed = parse_cpp_declarations(source, "dl_group.h");
20878        assert!(
20879            parsed
20880                .declarations()
20881                .iter()
20882                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
20883            "{:#?}",
20884            parsed.declarations()
20885        );
20886        assert_eq!(
20887            function_identities(&parsed),
20888            vec![
20889                (
20890                    "Botan.DL_Group.DL_Group".to_string(),
20891                    "(std::string_view)".to_string()
20892                ),
20893                (
20894                    "Botan.DL_Group.DL_Group".to_string(),
20895                    "(std::string_view, int)".to_string()
20896                ),
20897                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
20898            ],
20899            "{:#?}",
20900            parsed.declarations()
20901        );
20902    }
20903
20904    /// #2552 shape 3, the `= default` variant plus the access-label
20905    /// constructor. The attributed defaulted constructor separates cleanly, but
20906    /// it strands the next member in a bare `ERROR`, and a constructor written
20907    /// with a member-initializer list under `private:` dissolves into
20908    /// expression soup that the reparse recovers from its own source range.
20909    #[test]
20910    #[hardwon(
20911        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
20912    )]
20913    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
20914        let source = r#"namespace Botan {
20915class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
20916   public:
20917      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
20918      XMSS_Parameters(int oid, int len);
20919      size_t len() const;
20920
20921   private:
20922      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
20923            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
20924
20925      int m_oid;
20926      int m_wots_oid;
20927      size_t m_element_size;
20928      size_t m_tree_height;
20929};
20930}
20931"#;
20932        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
20933        let constructors = function_identities(&parsed)
20934            .into_iter()
20935            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
20936            .map(|(_, signature)| signature)
20937            .collect::<Vec<_>>();
20938        assert_eq!(
20939            constructors,
20940            vec![
20941                "()".to_string(),
20942                "(int, int)".to_string(),
20943                "(int, int, size_t, size_t)".to_string(),
20944            ],
20945            "{:#?}",
20946            parsed.declarations()
20947        );
20948    }
20949
20950    /// Negative control for the same rule: a real qualified name spells its
20951    /// `::` in the source, so the separator is present rather than MISSING and
20952    /// the out-of-line definition keeps its owner.
20953    #[test]
20954    #[hardwon(
20955        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
20956    )]
20957    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
20958        let source = r#"namespace shell {
20959struct Outer {
20960   struct Inner {
20961      Inner(int v);
20962      void run(int v);
20963   };
20964};
20965Outer::Inner::Inner(int v) {}
20966void Outer::Inner::run(int v) {}
20967}
20968"#;
20969        let parsed = parse_cpp_declarations(source, "outer.cpp");
20970        let names = function_identities(&parsed)
20971            .into_iter()
20972            .map(|(fq_name, _)| fq_name)
20973            .collect::<Vec<_>>();
20974        assert!(
20975            names
20976                .iter()
20977                .all(|name| name.starts_with("shell.Outer$Inner.")),
20978            "{names:#?}"
20979        );
20980    }
20981
20982    #[test]
20983    #[hardwon(
20984        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
20985    )]
20986    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
20987        let source = r#"namespace control {
20988template <typename T>
20989class AnySpan;
20990template <typename T>
20991class ABSL_ATTRIBUTE_VIEW AnySpan {
20992 public:
20993  int begin() const;
20994};
20995}
20996
20997namespace absl {
20998ABSL_NAMESPACE_BEGIN
20999template <typename T>
21000class ABSL_ATTRIBUTE_VIEW Span {
21001 public:
21002  int begin() const;
21003  int back() const;
21004};
21005
21006int begin();
21007int back();
21008}
21009"#;
21010        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
21011        let declarations = parsed.declarations();
21012        assert!(
21013            declarations
21014                .iter()
21015                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
21016        );
21017        for method in ["begin", "back"] {
21018            assert!(declarations.iter().any(|unit| {
21019                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
21020            }));
21021            assert!(
21022                declarations.iter().any(|unit| {
21023                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
21024                })
21025            );
21026        }
21027        assert!(
21028            declarations
21029                .iter()
21030                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
21031        );
21032        assert!(
21033            declarations
21034                .iter()
21035                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
21036        );
21037        assert!(
21038            declarations
21039                .iter()
21040                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
21041        );
21042    }
21043
21044    #[test]
21045    #[hardwon(
21046        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21047    )]
21048    fn explicit_global_member_definition_has_canonical_package_boundary() {
21049        let source = r#"
21050namespace arangodb::aql {
21051class ExecutionPlan {
21052 public:
21053  template<class... Args> Node* createNode(Args&&... args);
21054};
21055}
21056
21057template<class... Args>
21058Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
21059"#;
21060        let parsed = parse_cpp_declarations(source, "global-member.cpp");
21061
21062        assert!(parsed.declarations().iter().any(|unit| {
21063            unit.is_function()
21064                && unit.package_name() == "arangodb::aql"
21065                && unit.short_name() == "ExecutionPlan.createNode"
21066                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
21067        }));
21068    }
21069
21070    #[test]
21071    #[hardwon(
21072        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21073    )]
21074    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
21075        let source = r#"
21076#ifndef TINYXML2_INCLUDED
21077#define TINYXML2_INCLUDED
21078namespace tinyxml2 {
21079class TINYXML2_LIB XMLUtil {
21080 public:
21081  static const char* SkipWhiteSpace(const char* p) {
21082    while (*p) {
21083      if (*p == ' ') {
21084        ++p;
21085      }
21086    }
21087    return p;
21088  }
21089  static bool StringEqual(const char* p, const char* q) {
21090    return p == q;
21091  }
21092  class TINYXML2_LIB Helper {
21093   public:
21094    void Touch();
21095  };
21096  static void ToStr(int value, char* buffer);
21097 private:
21098  static const char* writeBoolTrue;
21099};
21100
21101class TINYXML2_LIB XMLNode {
21102 public:
21103  virtual XMLNode* ShallowClone() const = 0;
21104  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
21105};
21106}
21107#endif
21108"#;
21109        let mut parser = tree_sitter::Parser::new();
21110        parser
21111            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21112            .unwrap();
21113        let tree = parser.parse(source, None).unwrap();
21114        let mut boundary_found = false;
21115        walk_named_tree_preorder(tree.root_node(), true, |node| {
21116            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
21117                && name == "XMLUtil"
21118            {
21119                boundary_found = fragmented_export_sibling_class_boundary(
21120                    node,
21121                    source,
21122                    &ParentIndex::unindexed(),
21123                )
21124                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
21125                .is_some_and(|(_, name, _, _)| name == "XMLNode");
21126            }
21127            WalkControl::Continue
21128        });
21129        assert!(
21130            boundary_found,
21131            "fixture must exercise the recovered sibling boundary"
21132        );
21133
21134        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
21135        assert!(
21136            parsed
21137                .declarations()
21138                .iter()
21139                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
21140            "{:#?}",
21141            parsed.declarations()
21142        );
21143        assert!(
21144            parsed
21145                .declarations()
21146                .iter()
21147                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
21148            "{:#?}",
21149            parsed.declarations()
21150        );
21151        assert!(parsed.declarations().iter().any(|unit| {
21152            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
21153        }));
21154        assert!(
21155            parsed
21156                .declarations()
21157                .iter()
21158                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
21159        );
21160        assert!(
21161            parsed
21162                .declarations()
21163                .iter()
21164                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
21165        );
21166    }
21167
21168    #[test]
21169    #[hardwon(
21170        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21171    )]
21172    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
21173        // Clang's diagnostic suite intentionally contains this ill-formed
21174        // spelling. The analyzer must retain the parser's explicit-global AST
21175        // boundary instead of constructing `cwg311::::cwg311::X`.
21176        let parsed = parse_cpp_declarations(
21177            r#"
21178namespace cwg311 {
21179namespace X { namespace Y {} }
21180namespace ::cwg311::X {}
21181}
21182"#,
21183            "explicit-global-namespace.cpp",
21184        );
21185
21186        assert!(parsed.declarations().iter().any(|unit| {
21187            unit.kind() == CodeUnitType::Module
21188                && unit.short_name() == "cwg311::X"
21189                && unit.fq_name() == "cwg311::X"
21190        }));
21191        assert!(
21192            parsed
21193                .declarations()
21194                .iter()
21195                .all(|unit| !unit.short_name().contains("::::")),
21196            "recovered namespace names must not retain empty scope components: {:#?}",
21197            parsed.declarations()
21198        );
21199    }
21200
21201    #[test]
21202    #[hardwon(
21203        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21204    )]
21205    fn repeated_scope_separator_does_not_create_empty_function_owner() {
21206        let scope = ScopeInfo {
21207            package_name: "X".to_string(),
21208            module: None,
21209            class_unit: None,
21210            template_signature: None,
21211            template_metadata: None,
21212            declarations_are_fields: false,
21213            recovered_specialization_member_scope: false,
21214            visible_using_namespaces: Vec::new(),
21215        };
21216
21217        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
21218
21219        assert!(owner.is_none());
21220        assert_eq!(name, "doit");
21221        assert_eq!(package, "X");
21222    }
21223
21224    #[test]
21225    #[hardwon(
21226        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21227    )]
21228    fn trailing_decltype_expression_is_not_a_function_declarator() {
21229        let source = r#"
21230namespace boost { namespace detail {
21231#if ! defined(BOOST_NO_SFINAE_EXPR) && \
21232    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
21233    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
21234#define BOOST_THREAD_PROVIDES_INVOKE
21235#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
21236template <class Fp, class A0, class ...Args>
21237inline auto
21238invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
21239       BOOST_THREAD_RV_REF(Args) ...args)
21240    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
21241{
21242    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
21243}
21244#endif
21245#endif
21246}}
21247"#;
21248        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
21249
21250        assert!(
21251            parsed
21252                .declarations()
21253                .iter()
21254                .all(|unit| unit.short_name() != ".*f")
21255        );
21256    }
21257
21258    fn find_class_named<'tree>(
21259        root: Node<'tree>,
21260        source: &str,
21261        expected_name: &str,
21262    ) -> Option<Node<'tree>> {
21263        let mut stack = vec![root];
21264        while let Some(node) = stack.pop() {
21265            if node.kind() == "class_specifier"
21266                && node
21267                    .child_by_field_name("name")
21268                    .is_some_and(|name| node_text(name, source) == expected_name)
21269            {
21270                return Some(node);
21271            }
21272            let mut cursor = node.walk();
21273            stack.extend(node.named_children(&mut cursor));
21274        }
21275        None
21276    }
21277
21278    #[test]
21279    #[hardwon(
21280        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21281    )]
21282    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
21283        let source = r#"EXPORT void definition(struct Value value) {}
21284EXPORT void prototype(struct Value value);
21285"#;
21286        let mut parser = tree_sitter::Parser::new();
21287        parser
21288            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21289            .unwrap();
21290        let tree = parser.parse(source, None).unwrap();
21291        let root = tree.root_node();
21292        let mut cursor = root.walk();
21293        let callables = root
21294            .named_children(&mut cursor)
21295            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
21296            .collect::<Vec<_>>();
21297
21298        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
21299        for callable in callables {
21300            assert!(callable.has_error(), "fixture must exercise error recovery");
21301            assert!(
21302                cpp_sentinel_macro_parts(callable, source).is_none(),
21303                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
21304            );
21305        }
21306    }
21307
21308    #[test]
21309    #[hardwon(
21310        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21311    )]
21312    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
21313        let source = r#"namespace absl {
21314ABSL_NAMESPACE_BEGIN
21315// Generate a floating-point variate conforming to a Beta distribution:
21316template <typename RealType = double>
21317class beta_distribution {
21318 public:
21319  using result_type = RealType;
21320
21321
21322  beta_distribution() : beta_distribution(1) {}
21323
21324  explicit beta_distribution(result_type alpha, result_type beta = 1)
21325      : param_(alpha, beta) {}
21326
21327  explicit beta_distribution(const param_type& p) : param_(p) {}
21328
21329  void reset() {}
21330
21331  // Generating functions
21332  template <typename URBG>
21333  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
21334    return (*this)(g, param_);
21335  }
21336
21337};
21338ABSL_NAMESPACE_END
21339}  // namespace absl
21340"#;
21341        let mut parser = tree_sitter::Parser::new();
21342        parser
21343            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21344            .unwrap();
21345        let tree = parser.parse(source, None).unwrap();
21346        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
21347        let body = namespace
21348            .child_by_field_name("body")
21349            .expect("fixture namespace body");
21350        let sentinel = body.named_child(0).expect("sentinel envelope");
21351        let callable = sentinel
21352            .child_by_field_name("declarator")
21353            .and_then(extract_function_declarator)
21354            .and_then(cpp_function_declarator_name_node)
21355            .expect("preserved callable name");
21356
21357        assert_eq!(sentinel.kind(), "function_definition");
21358        assert_eq!(callable.kind(), "operator_name");
21359        assert!(
21360            cpp_sentinel_macro_parts(sentinel, source).is_some(),
21361            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
21362        );
21363    }
21364
21365    #[test]
21366    #[hardwon(
21367        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21368    )]
21369    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
21370        let source = r#"namespace absl {
21371ABSL_NAMESPACE_BEGIN
21372// absl::discrete_distribution
21373//
21374// A discrete distribution produces random integers i, where 0 <= i < n
21375template <typename IntType = int>
21376class discrete_distribution {
21377 public:
21378  using result_type = IntType;
21379  class param_type {
21380   public:
21381    param_type() { init(); }
21382    template <typename InputIterator>
21383    explicit param_type(InputIterator begin, InputIterator end)
21384        : p_(begin, end) {
21385      init();
21386    }
21387  };
21388  discrete_distribution() : param_() {}
21389  explicit discrete_distribution(const param_type& p) : param_(p) {}
21390};
21391ABSL_NAMESPACE_END
21392}  // namespace absl
21393"#;
21394        let mut parser = tree_sitter::Parser::new();
21395        parser
21396            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21397            .unwrap();
21398        let tree = parser.parse(source, None).unwrap();
21399        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
21400        let body = namespace
21401            .child_by_field_name("body")
21402            .expect("fixture namespace body");
21403        let sentinel = body.named_child(0).expect("sentinel envelope");
21404        let callable = sentinel
21405            .child_by_field_name("declarator")
21406            .and_then(extract_function_declarator)
21407            .and_then(cpp_function_declarator_name_node)
21408            .expect("preserved callable name");
21409
21410        assert_eq!(sentinel.kind(), "function_definition");
21411        assert_eq!(callable.kind(), "identifier");
21412        assert!(
21413            cpp_sentinel_macro_parts(sentinel, source).is_some(),
21414            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
21415        );
21416    }
21417
21418    #[test]
21419    #[hardwon(
21420        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21421    )]
21422    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
21423        let source = r#"
21424#define CPPCHECKLIB
21425class Library {
21426    struct Container {
21427        CPPCHECKLIB static std::string toString(Yield yield);
21428        CPPCHECKLIB static std::string toString(Action action);
21429    };
21430};
21431"#;
21432        let mut parser = tree_sitter::Parser::new();
21433        parser
21434            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21435            .unwrap();
21436        let tree = parser.parse(source, None).unwrap();
21437        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
21438        let parsed = parse_cpp_file(&file, source, &tree);
21439        assert!(
21440            parsed
21441                .declarations()
21442                .iter()
21443                .all(|unit| unit.fq_name() != "Library$Container.std"),
21444            "the qualified return-type namespace must not become a field: {:#?}",
21445            parsed.declarations()
21446        );
21447        for expected in ["(Yield)", "(Action)"] {
21448            assert!(
21449                parsed.declarations().iter().any(|unit| {
21450                    unit.is_function()
21451                        && unit.fq_name() == "Library$Container.toString"
21452                        && unit.signature() == Some(expected)
21453                }),
21454                "recovered toString overload {expected} is missing: {:#?}",
21455                parsed.declarations()
21456            );
21457        }
21458    }
21459
21460    #[test]
21461    #[hardwon(
21462        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21463    )]
21464    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
21465        let source = r#"
21466#define SIMPLECPP_LIB
21467namespace simplecpp {
21468using TokenString = std::string;
21469struct Location { int line{}; };
21470class SIMPLECPP_LIB Token {
21471  TokenString prefix;
21472  void prefix_method() {}
21473 public:
21474  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
21475      whitespaceahead(wsahead), location(loc), string(s)
21476      // The comment must not hide the constructor body from recovery.
21477      {
21478      flags();
21479  }
21480  TokenString string;
21481  bool whitespaceahead;
21482  Location location;
21483  Token *previous{};
21484 private:
21485  void flags() {
21486      whitespaceahead = true;
21487  }
21488};
21489}
21490"#;
21491        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
21492
21493        let location_fields = parsed
21494            .declarations()
21495            .iter()
21496            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
21497            .collect::<Vec<_>>();
21498        assert_eq!(
21499            location_fields.len(),
21500            1,
21501            "location should have one class-owned declaration: {:#?}",
21502            parsed.declarations()
21503        );
21504        assert!(
21505            location_fields[0].is_field(),
21506            "location has wrong kind: {:#?}",
21507            parsed.declarations()
21508        );
21509        assert!(
21510            parsed.declarations().iter().all(|unit| {
21511                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
21512            })
21513        );
21514        assert!(
21515            parsed.declarations().iter().all(|unit| {
21516                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
21517            })
21518        );
21519        assert!(
21520            parsed
21521                .declarations()
21522                .iter()
21523                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
21524        );
21525        assert!(
21526            parsed
21527                .declarations()
21528                .iter()
21529                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
21530            "the recovered class must retain its constructor: {:#?}",
21531            parsed.declarations()
21532        );
21533        assert!(
21534            parsed
21535                .declarations()
21536                .iter()
21537                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
21538        );
21539        assert!(parsed.declarations().iter().any(|unit| {
21540            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
21541        }));
21542        let constructor = parsed
21543            .declarations()
21544            .iter()
21545            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
21546            .expect("recovered constructor");
21547        let constructor_start = source.find("Token(const").expect("constructor start");
21548        let constructor_end = source
21549            .get(
21550                ..source
21551                    .find("  TokenString string;")
21552                    .expect("constructor end"),
21553            )
21554            .expect("constructor slice")
21555            .trim_end()
21556            .len();
21557        assert!(
21558            parsed
21559                .navigation_ranges
21560                .get(constructor)
21561                .is_some_and(|ranges| {
21562                    ranges.iter().any(|range| {
21563                        range.start_byte == constructor_start && range.end_byte == constructor_end
21564                    })
21565                }),
21566            "constructor navigation must span the full body: {:#?}",
21567            parsed.navigation_ranges
21568        );
21569        assert_eq!(
21570            parsed
21571                .signature_metadata
21572                .get(constructor)
21573                .and_then(|metadata| metadata.first())
21574                .and_then(SignatureMetadata::callable_linkage),
21575            Some(CallableLinkage::External)
21576        );
21577        let token_class = parsed
21578            .declarations()
21579            .iter()
21580            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
21581            .expect("recovered Token class");
21582        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
21583        assert!(
21584            parsed
21585                .navigation_ranges
21586                .get(token_class)
21587                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
21588            "class navigation must include the terminating semicolon: {:#?}",
21589            parsed.navigation_ranges
21590        );
21591    }
21592
21593    #[test]
21594    #[hardwon(
21595        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21596    )]
21597    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
21598        let source = r#"
21599#define SIMPLECPP_LIB
21600namespace simplecpp {
21601using TokenString = std::string;
21602class Macro;
21603struct Location {
21604  unsigned int fileIndex{};
21605  unsigned int line{};
21606  unsigned int col{};
21607};
21608struct Output {
21609  int type;
21610};
21611class SIMPLECPP_LIB Token {
21612 public:
21613  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
21614      whitespaceahead(wsahead), location(loc), string(s) {
21615      flags();
21616  }
21617  Token(const Token &tok) :
21618      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
21619      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
21620      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
21621  Token &operator=(const Token &tok) = delete;
21622  const TokenString& str() const { return string; }
21623  void setstr(const std::string &s) { string = s; flags(); }
21624  bool isOneOf(const char ops[]) const;
21625  TokenString macro;
21626  char op;
21627  bool comment;
21628  bool name;
21629  bool number;
21630  bool whitespaceahead;
21631  Location location;
21632  Token *previous{};
21633  Token *next{};
21634 private:
21635  void flags() {
21636      name = !string.empty();
21637      comment = false;
21638      number = false;
21639      op = 0;
21640  }
21641  TokenString string;
21642};
21643}
21644struct Following {
21645  int type;
21646};
21647class SIMPLECPP_LIB Later {
21648 public:
21649  Later(int value) : value(value) {}
21650  int value;
21651};
21652"#;
21653        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
21654        assert!(
21655            parsed
21656                .declarations()
21657                .iter()
21658                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
21659        );
21660        assert!(
21661            !parsed
21662                .declarations()
21663                .iter()
21664                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
21665        );
21666        assert!(
21667            parsed
21668                .declarations()
21669                .iter()
21670                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
21671        );
21672        assert!(
21673            !parsed
21674                .declarations()
21675                .iter()
21676                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
21677        );
21678        assert!(
21679            parsed
21680                .declarations()
21681                .iter()
21682                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
21683        );
21684        assert!(
21685            parsed
21686                .declarations()
21687                .iter()
21688                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
21689        );
21690        assert!(
21691            parsed
21692                .declarations()
21693                .iter()
21694                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
21695        );
21696        assert!(
21697            parsed
21698                .declarations()
21699                .iter()
21700                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
21701        );
21702        assert!(
21703            parsed
21704                .declarations()
21705                .iter()
21706                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
21707        );
21708        assert!(
21709            parsed
21710                .declarations()
21711                .iter()
21712                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
21713        );
21714        assert!(parsed.declarations().iter().all(|unit| {
21715            !matches!(
21716                unit.fq_name().as_str(),
21717                "simplecpp.Token.Following" | "simplecpp.Token.Later"
21718            )
21719        }));
21720        assert!(
21721            !parsed
21722                .declarations()
21723                .iter()
21724                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
21725            "the following struct must remain outside the recovered Token class"
21726        );
21727    }
21728
21729    #[test]
21730    #[hardwon(
21731        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21732    )]
21733    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
21734        let source = r#"
21735#define SIMPLECPP_LIB
21736namespace {
21737namespace simplecpp {
21738using TokenString = std::string;
21739struct Location { int line{}; };
21740class SIMPLECPP_LIB HiddenToken {
21741 public:
21742  HiddenToken(const TokenString &s, const Location &loc) :
21743      location(loc), string(s) {
21744      flags();
21745  }
21746  TokenString string;
21747  Location location;
21748  HiddenToken *previous{};
21749 private:
21750  void flags() {}
21751};
21752}
21753}
21754"#;
21755        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
21756        let constructor = parsed
21757            .declarations()
21758            .iter()
21759            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
21760            .expect("recovered anonymous-namespace constructor");
21761        assert_eq!(
21762            parsed
21763                .signature_metadata
21764                .get(constructor)
21765                .and_then(|metadata| metadata.first())
21766                .and_then(SignatureMetadata::callable_linkage),
21767            Some(CallableLinkage::Internal)
21768        );
21769    }
21770
21771    #[test]
21772    #[hardwon(
21773        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21774    )]
21775    fn macro_qualified_static_field_keeps_real_declarator() {
21776        let source = r#"#define JSON_INLINE_VARIABLE
21777struct Reader {
21778static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
21779static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
21780static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
21781};"#;
21782        let mut parser = tree_sitter::Parser::new();
21783        parser
21784            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21785            .unwrap();
21786        let tree = parser.parse(source, None).unwrap();
21787        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
21788        let parsed = parse_cpp_file(&file, source, &tree);
21789        for expected in [
21790            "Reader.npos",
21791            "Reader.other",
21792            "Reader.pointer",
21793            "Reader.reference",
21794        ] {
21795            assert!(
21796                parsed
21797                    .declarations()
21798                    .iter()
21799                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
21800                "real macro-decorated field {expected} is missing: {:#?}",
21801                parsed.declarations()
21802            );
21803        }
21804        assert!(
21805            parsed
21806                .declarations()
21807                .iter()
21808                .all(|unit| unit.fq_name() != "Reader.std"),
21809            "qualified type prefix became a pseudo-field: {:#?}",
21810            parsed.declarations()
21811        );
21812        let root = tree.root_node();
21813        let mut stack = vec![root];
21814        let mut signatures = Vec::new();
21815        while let Some(current) = stack.pop() {
21816            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
21817            {
21818                signatures.extend(
21819                    declarators
21820                        .into_iter()
21821                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
21822                );
21823            }
21824            let mut cursor = current.walk();
21825            stack.extend(current.named_children(&mut cursor));
21826        }
21827        signatures.sort();
21828        assert_eq!(
21829            signatures,
21830            [
21831                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
21832                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
21833                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
21834                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
21835            ]
21836        );
21837    }
21838
21839    fn member_function_linkage(source: &str) -> CallableLinkage {
21840        let mut parser = tree_sitter::Parser::new();
21841        parser
21842            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21843            .unwrap();
21844        let tree = parser.parse(source, None).unwrap();
21845        let ancestry = ParentIndex::new(tree.root_node());
21846        let mut stack = vec![tree.root_node()];
21847        while let Some(node) = stack.pop() {
21848            if node.kind() == "function_definition" {
21849                let mut current = node.parent();
21850                while let Some(parent) = current {
21851                    if matches!(
21852                        parent.kind(),
21853                        "class_specifier" | "struct_specifier" | "union_specifier"
21854                    ) {
21855                        return cpp_callable_linkage(node, source, &ancestry);
21856                    }
21857                    current = parent.parent();
21858                }
21859            }
21860            let mut cursor = node.walk();
21861            stack.extend(node.named_children(&mut cursor));
21862        }
21863        panic!("fixture has no member function definition");
21864    }
21865
21866    #[test]
21867    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
21868        assert_eq!(
21869            member_function_linkage("struct Named { int method() { return 1; } };"),
21870            CallableLinkage::External
21871        );
21872        assert_eq!(
21873            member_function_linkage(
21874                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
21875            ),
21876            CallableLinkage::Internal
21877        );
21878        assert_eq!(
21879            member_function_linkage("struct { int method() { return 1; } } instance;"),
21880            CallableLinkage::Internal
21881        );
21882        assert_eq!(
21883            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
21884            CallableLinkage::Internal
21885        );
21886    }
21887
21888    #[test]
21889    #[hardwon(
21890        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21891    )]
21892    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
21893        let source = r#"
21894#ifndef PROTON_VALUE_HPP
21895#define PROTON_VALUE_HPP
21896namespace proton {
21897namespace internal {
21898class value_base {
21899  protected:
21900    internal::data& data();
21901    internal::data data_;
21902  friend class codec::encoder;
21903  friend class codec::decoder;
21904};
21905}
21906class value : public internal::value_base, private internal::comparable<value> {
21907  private:
21908    template<class T, class U=void> struct assignable :
21909        public std::enable_if<codec::is_encodable<T>::value, U> {};
21910    template<class U> struct assignable<value, U> {};
21911  public:
21912    PN_CPP_EXTERN value();
21913    PN_CPP_EXTERN value(const value&);
21914    PN_CPP_EXTERN value& operator=(const value&);
21915    PN_CPP_EXTERN value(value&&);
21916    PN_CPP_EXTERN value& operator=(value&&);
21917    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
21918    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
21919        codec::encoder e(*this);
21920        e << x;
21921        return *this;
21922    }
21923    PN_CPP_EXTERN type_id type() const;
21924    PN_CPP_EXTERN bool empty() const;
21925    PN_CPP_EXTERN void clear();
21926    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
21927    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
21928  friend PN_CPP_EXTERN void swap(value&, value&);
21929  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
21930  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
21931  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
21932    value(pn_data_t* d);
21933    void reset(pn_data_t* d = 0);
21934};
21935}
21936#endif
21937"#;
21938        let mut parser = tree_sitter::Parser::new();
21939        parser
21940            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21941            .unwrap();
21942        let tree = parser.parse(source, None).unwrap();
21943        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
21944        let parsed = parse_cpp_file(&file, source, &tree);
21945        let macro_constructors = parsed
21946            .signature_metadata
21947            .iter()
21948            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
21949            .flat_map(|(_, metadata)| metadata)
21950            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
21951            .collect::<Vec<_>>();
21952
21953        assert_eq!(
21954            macro_constructors.len(),
21955            3,
21956            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
21957            parsed.declarations()
21958        );
21959        assert!(
21960            macro_constructors.iter().all(|metadata| {
21961                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
21962            }),
21963            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
21964        );
21965    }
21966
21967    #[test]
21968    #[hardwon(
21969        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
21970    )]
21971    fn recovered_export_class_typedef_uses_displaced_alias_name() {
21972        let source = r#"
21973namespace spi {
21974class Filter {
21975public:
21976    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
21977};
21978}
21979namespace filter {
21980class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
21981{
21982public:
21983    typedef spi::Filter BASE_CLASS;
21984    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
21985    BEGIN_LOG4CXX_CAST_MAP()
21986    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
21987    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
21988    END_LOG4CXX_CAST_MAP()
21989    FilterDecision decide() const;
21990};
21991}
21992"#;
21993        let mut parser = tree_sitter::Parser::new();
21994        parser
21995            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21996            .unwrap();
21997        let tree = parser.parse(source, None).unwrap();
21998        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
21999        let parsed = parse_cpp_file(&file, source, &tree);
22000        assert!(
22001            parsed.declarations().iter().any(|unit| {
22002                unit.is_class()
22003                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
22004                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
22005            }),
22006            "the displaced typedef alias must retain its declared name: {:#?}",
22007            parsed.declarations()
22008        );
22009        assert!(
22010            parsed
22011                .declarations()
22012                .iter()
22013                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
22014            "the qualified underlying type must not become a false nested alias: {:#?}",
22015            parsed.declarations()
22016        );
22017    }
22018
22019    #[test]
22020    #[hardwon(
22021        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22022    )]
22023    fn exported_single_base_recovery_uses_displaced_class_name() {
22024        let source = r#"
22025class CORE_EXPORT QgsPoint : public AbstractGeometry
22026{
22027    Q_GADGET
22028
22029    Q_PROPERTY( double x READ x WRITE setX )
22030    Q_PROPERTY( double y READ y WRITE setY )
22031    Q_PROPERTY( double z READ z WRITE setZ )
22032    Q_PROPERTY( double m READ m WRITE setM )
22033
22034  public:
22035#ifndef SIP_RUN
22036    QgsPoint(
22037      double x = std::numeric_limits<double>::quiet_NaN(),
22038      double y = std::numeric_limits<double>::quiet_NaN(),
22039      double z = std::numeric_limits<double>::quiet_NaN(),
22040      double m = std::numeric_limits<double>::quiet_NaN(),
22041      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
22042    );
22043#else
22044    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 )];
22045    % MethodCode
22046    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
22047    {
22048      int state;
22049      sipIsErr = 0;
22050      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
22051      if ( !sipIsErr )
22052      {
22053        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
22054      }
22055      sipReleaseType( p, sipType_QgsPointXY, state );
22056    }
22057    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
22058    {
22059      int state;
22060      sipIsErr = 0;
22061
22062      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
22063      if ( !sipIsErr )
22064      {
22065        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
22066      }
22067      sipReleaseType( p, sipType_QPointF, state );
22068    }
22069    else if (
22070      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
22071      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
22072      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
22073      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
22074    {
22075      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
22076      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
22077      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
22078      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
22079      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
22080      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
22081    }
22082    else // Invalid ctor arguments
22083    {
22084      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
22085      sipIsErr = 1;
22086    }
22087    % End
22088#endif
22089
22090    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
22091    explicit QgsPoint( QPointF p ) SIP_SKIP;
22092    explicit QgsPoint(
22093      Qgis::WkbType wkbType,
22094      double x = std::numeric_limits<double>::quiet_NaN(),
22095      double y = std::numeric_limits<double>::quiet_NaN(),
22096      double z = std::numeric_limits<double>::quiet_NaN(),
22097      double m = std::numeric_limits<double>::quiet_NaN()
22098    ) SIP_SKIP;
22099    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
22100    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
22101    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
22102#ifndef SIP_RUN
22103  private:
22104    bool fuzzyHelper(
22105      double epsilon,
22106      const AbstractGeometry &other,
22107      bool is3DFlag,
22108      bool isMeasureFlag
22109    ) const
22110    {
22111      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
22112    }
22113#endif
22114};
22115class Ordinary : public Base { public: Ordinary(); };
22116class API_EXPORT Plain { public: Plain(); };
22117class API_EXPORT : public Base {};
22118class
22119PN_CPP_CLASS_EXTERN Sender : public Link {
22120    Sender();
22121    struct impl;
22122    struct impl& get_impl() const;
22123};
22124class thread_ctx_t {};
22125class ctx_t ZMQ_FINAL : public thread_ctx_t {
22126    bool start();
22127};
22128"#;
22129        let mut parser = tree_sitter::Parser::new();
22130        parser
22131            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22132            .unwrap();
22133        let tree = parser.parse(source, None).unwrap();
22134        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
22135        let parsed = parse_cpp_file(&file, source, &tree);
22136        let declarations = parsed.declarations();
22137
22138        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
22139            assert!(
22140                declarations
22141                    .iter()
22142                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
22143                "missing recovered class {expected}: {declarations:#?}"
22144            );
22145        }
22146        let qgs_point = declarations
22147            .iter()
22148            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
22149            .expect("recovered QgsPoint class");
22150        assert_eq!(
22151            parsed.raw_supertypes.get(qgs_point),
22152            Some(&vec!["AbstractGeometry".to_string()]),
22153            "single-base export recovery must retain its displaced base"
22154        );
22155        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
22156        assert!(
22157            parsed
22158                .navigation_ranges
22159                .get(qgs_point)
22160                .is_some_and(|ranges| {
22161                    !ranges.is_empty()
22162                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
22163                }),
22164            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
22165            parsed.navigation_ranges.get(qgs_point)
22166        );
22167        let sender = declarations
22168            .iter()
22169            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
22170            .expect("recovered Sender class");
22171        assert_eq!(
22172            parsed.raw_supertypes.get(sender),
22173            Some(&vec!["Link".to_string()]),
22174            "post-declarator export recovery must retain its displaced base"
22175        );
22176        let recovered_member = declarations
22177            .iter()
22178            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
22179            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
22180        assert_eq!(
22181            parsed
22182                .signature_metadata
22183                .get(recovered_member)
22184                .and_then(|metadata| metadata.first())
22185                .and_then(SignatureMetadata::callable_linkage),
22186            Some(CallableLinkage::External),
22187            "a named recovered class's members have external linkage"
22188        );
22189        let ctx = declarations
22190            .iter()
22191            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
22192            .expect("recovered ctx_t class");
22193        assert_eq!(
22194            parsed.raw_supertypes.get(ctx),
22195            Some(&vec!["thread_ctx_t".to_string()]),
22196            "postfix export-macro recovery must retain its displaced base"
22197        );
22198        assert!(
22199            declarations.iter().any(|unit| {
22200                unit.is_function()
22201                    && unit.fq_name() == "QgsPoint.QgsPoint"
22202                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
22203            }),
22204            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
22205        );
22206        assert!(
22207            declarations.iter().all(|unit| {
22208                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
22209            }),
22210            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
22211        );
22212    }
22213
22214    #[test]
22215    #[hardwon(
22216        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22217    )]
22218    fn function_like_export_macro_classes_keep_names_and_base_edges() {
22219        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
22220        // error: a plain body, a single base, `final` without a base, `final`
22221        // with one base, and `final` with a comma-separated base list.
22222        let source = r#"
22223namespace api {
22224class PROJECT_PUBLIC_API(2, 0) Prelude {
22225  public:
22226    Prelude();
22227};
22228class PROJECT_PUBLIC_API(2, 0) Base {
22229  public:
22230    Base(int value);
22231};
22232class PROJECT_PUBLIC_API(2, 0) Mixin {
22233  public:
22234    Mixin();
22235};
22236class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
22237  public:
22238    Adopted(int value);
22239};
22240class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
22241  public:
22242    Derived(int value);
22243};
22244class PROJECT_PUBLIC_API(2, 0) Solo final {
22245  public:
22246    Solo();
22247};
22248class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
22249  public:
22250    Blended(int value);
22251};
22252class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
22253  public:
22254    Woven(int value);
22255};
22256} // namespace api
22257"#;
22258        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
22259        let declarations = parsed.declarations();
22260        let class_named = |name: &str| {
22261            declarations
22262                .iter()
22263                .find(|unit| unit.is_class() && unit.fq_name() == name)
22264                .unwrap_or_else(|| {
22265                    panic!("missing function-like export macro class {name}: {declarations:#?}")
22266                })
22267        };
22268        let base = class_named("api.Base");
22269        class_named("api.Prelude");
22270        class_named("api.Mixin");
22271
22272        assert_eq!(
22273            parsed.raw_supertypes.get(class_named("api.Adopted")),
22274            Some(&vec!["Base".to_string()])
22275        );
22276        assert_eq!(
22277            parsed.raw_supertypes.get(class_named("api.Derived")),
22278            Some(&vec!["Base".to_string()])
22279        );
22280        assert_eq!(
22281            parsed.raw_supertypes.get(class_named("api.Solo")),
22282            None,
22283            "a final class without a base list must not invent a supertype"
22284        );
22285        assert_eq!(
22286            parsed.raw_supertypes.get(class_named("api.Blended")),
22287            Some(&vec!["Base".to_string(), "Mixin".to_string()])
22288        );
22289        assert_eq!(
22290            parsed.raw_supertypes.get(class_named("api.Woven")),
22291            Some(&vec!["Base".to_string(), "Mixin".to_string()])
22292        );
22293        assert!(
22294            declarations
22295                .iter()
22296                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22297            "the export macro must not become a declaration: {declarations:#?}"
22298        );
22299        assert!(
22300            declarations.iter().all(|unit| !matches!(
22301                unit.identifier(),
22302                "final" | "public" | "protected" | "private"
22303            )),
22304            "the head specifiers must not become declarations: {declarations:#?}"
22305        );
22306        assert!(
22307            parsed
22308                .navigation_ranges
22309                .get(base)
22310                .is_some_and(|ranges| !ranges.is_empty()),
22311            "the recovered base must retain a navigable declaration range"
22312        );
22313    }
22314
22315    #[test]
22316    #[hardwon(
22317        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
22318    )]
22319    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
22320        // #2557: the class name is the last identifier before the head ends
22321        // (`final`, the base clause `:`, or the body), whatever its spelling.
22322        // A class named in capitals (`X509_CA`) is a class, and an object-like
22323        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
22324        // the control whose spelling never mattered.
22325        let source = r#"
22326namespace api {
22327class PROJECT_PUBLIC_API(2, 0) Base {
22328  public:
22329    Base();
22330};
22331class PROJECT_PUBLIC_API(2, 0) Mixin {
22332  public:
22333    Mixin();
22334};
22335class PROJECT_PUBLIC_API(2, 0) Name {
22336  public:
22337    Name();
22338};
22339class PROJECT_PUBLIC_API(2, 0) X509_CA final {
22340  public:
22341    X509_CA();
22342};
22343class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
22344  public:
22345    HSS_LMS_KEY();
22346};
22347class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
22348  public:
22349    GOST_3410();
22350};
22351class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
22352  public:
22353    PKCS11_RSA();
22354};
22355class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
22356  public:
22357    Plain();
22358};
22359class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
22360  public:
22361    Decorated();
22362};
22363class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
22364  public:
22365    Layered();
22366};
22367} // namespace api
22368"#;
22369        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
22370        let declarations = parsed.declarations();
22371        let class_named = |name: &str| {
22372            declarations
22373                .iter()
22374                .find(|unit| unit.is_class() && unit.fq_name() == name)
22375                .unwrap_or_else(|| {
22376                    panic!("missing function-like export macro class {name}: {declarations:#?}")
22377                })
22378        };
22379        for (name, bases) in [
22380            ("api.Name", None),
22381            ("api.X509_CA", None),
22382            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
22383            ("api.GOST_3410", Some(vec!["Base"])),
22384            ("api.PKCS11_RSA", None),
22385            ("api.Plain", None),
22386            ("api.Decorated", Some(vec!["Base"])),
22387            ("api.Layered", Some(vec!["Base", "Mixin"])),
22388        ] {
22389            let expected =
22390                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
22391            assert_eq!(
22392                parsed.raw_supertypes.get(class_named(name)),
22393                expected.as_ref(),
22394                "{name}"
22395            );
22396        }
22397        assert!(
22398            declarations.iter().all(|unit| !matches!(
22399                unit.identifier(),
22400                "PROJECT_PUBLIC_API"
22401                    | "OTHER_MACRO"
22402                    | "FIRST_MACRO"
22403                    | "SECOND_MACRO"
22404                    | "final"
22405                    | "public"
22406            )),
22407            "macros and head specifiers must not become declarations: {declarations:#?}"
22408        );
22409    }
22410
22411    #[test]
22412    #[hardwon(
22413        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
22414    )]
22415    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
22416        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
22417        // head, the body's `{` and its first member in one declaration-scope
22418        // ERROR, scatters the remaining members across the container's
22419        // siblings, and ends the container on the class's own `}`. The class
22420        // was lost and the macro minted a class of its own.
22421        let source = r#"
22422namespace api {
22423
22424/**
22425* Doc comment
22426*/
22427class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22428   public:
22429      /**
22430      * Construct from a point.
22431      */
22432      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22433
22434#if defined(PROJECT_HAS_LEGACY_POINT)
22435      /**
22436      * Construct from a legacy point.
22437      */
22438      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22439#endif
22440
22441      std::string algo_name() const override;
22442
22443      AlgorithmIdentifier algorithm_identifier() const override;
22444};
22445
22446}
22447"#;
22448        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
22449        let declarations = parsed.declarations();
22450        let class = declarations
22451            .iter()
22452            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
22453            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22454        assert_eq!(
22455            parsed.raw_supertypes.get(class),
22456            Some(&vec!["BaseKey".to_string()]),
22457            "the virtual base is the class's base: {declarations:#?}"
22458        );
22459        for member in [
22460            "api.VirtualBased.algo_name",
22461            "api.VirtualBased.algorithm_identifier",
22462        ] {
22463            assert!(
22464                declarations
22465                    .iter()
22466                    .any(|unit| unit.is_function() && unit.fq_name() == member),
22467                "{member} must be owned by the recovered class: {declarations:#?}"
22468            );
22469        }
22470        assert!(
22471            declarations
22472                .iter()
22473                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22474            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
22475        );
22476    }
22477
22478    #[test]
22479    #[hardwon(
22480        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
22481    )]
22482    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
22483        // #2924: object-like macro lines before the head demote the `class`
22484        // keyword to a bare identifier and make the macro invocation an
22485        // `init_declarator`, so the head arrives as a `declaration` rather than
22486        // as an ERROR carrying a `class_specifier`. The name is still the last
22487        // identifier before `final`, and `virtual` is a base specifier rather
22488        // than a base.
22489        let source = r#"
22490namespace api {
22491
22492DIAGNOSTIC_PUSH
22493DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
22494
22495class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
22496                                               public virtual api::Inner::Key {
22497   public:
22498      std::string algo_name() const override;
22499};
22500
22501DIAGNOSTIC_POP
22502
22503}
22504"#;
22505        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
22506        let declarations = parsed.declarations();
22507        let class = declarations
22508            .iter()
22509            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
22510            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22511        assert_eq!(
22512            parsed.raw_supertypes.get(class),
22513            Some(&vec![
22514                "api::Outer::Key".to_string(),
22515                "api::Inner::Key".to_string()
22516            ]),
22517            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
22518        );
22519        assert!(
22520            declarations
22521                .iter()
22522                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
22523            "the member is owned by the recovered class: {declarations:#?}"
22524        );
22525        assert!(
22526            declarations
22527                .iter()
22528                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22529            "the macro invocation must not mint a declaration: {declarations:#?}"
22530        );
22531    }
22532
22533    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
22534        format!(
22535            r#"
22536namespace api {{
22537class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
22538   public:
22539      /** Return a descriptive string. */
22540      const char* what() const noexcept override {{ return m_msg.c_str(); }}
22541
22542      /** Return the type of error. */
22543      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
22544
22545      /** Return an associated error code. */
22546      virtual int error_code() const noexcept {{ return 0; }}
22547
22548      /** Avoid throwing the base directly. */
22549      explicit Exception(std::string_view msg);
22550
22551      /** Avoid throwing the base directly. */
22552      Exception(const char* prefix, std::string_view msg);
22553
22554      /** Avoid throwing the base directly. */
22555      Exception(std::string_view msg, const std::exception& e);
22556
22557   private:
22558      std::string m_msg;
22559}};
22560
22561class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
22562   public:
22563      explicit {name}(std::string_view msg);
22564
22565      explicit {name}(std::string_view msg, std::string_view where);
22566
22567      {name}(std::string_view msg, const std::exception& e);
22568
22569      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
22570}};
22571}} // namespace api
22572"#
22573        )
22574    }
22575
22576    #[test]
22577    #[hardwon(
22578        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
22579    )]
22580    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
22581        // The class embedded in a preceding malformed body follows the same
22582        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
22583        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
22584            let source = function_like_export_class_fixture(head, name);
22585            let mut parser = tree_sitter::Parser::new();
22586            parser
22587                .set_language(&tree_sitter_cpp::LANGUAGE.into())
22588                .expect("set C++ grammar");
22589            let tree = parser.parse(&source, None).expect("parse fixture");
22590            let mut embedded = Vec::new();
22591            let mut stack = vec![tree.root_node()];
22592            while let Some(node) = stack.pop() {
22593                embedded.extend(
22594                    recover_embedded_function_like_export_classes(node, &source)
22595                        .into_iter()
22596                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
22597                );
22598                let mut cursor = node.walk();
22599                stack.extend(node.named_children(&mut cursor));
22600            }
22601            assert!(
22602                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
22603                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
22604                tree.root_node().to_sexp()
22605            );
22606            assert!(
22607                embedded
22608                    .iter()
22609                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
22610                "{head}: the object-like macro is not a class: {embedded:#?}"
22611            );
22612
22613            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
22614            let declarations = parsed.declarations();
22615            let class = declarations
22616                .iter()
22617                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22618                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
22619            assert_eq!(
22620                parsed.raw_supertypes.get(class),
22621                Some(&vec!["Exception".to_string()]),
22622                "{head}"
22623            );
22624            assert!(
22625                declarations
22626                    .iter()
22627                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
22628                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
22629            );
22630        }
22631    }
22632
22633    #[test]
22634    #[hardwon(
22635        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
22636    )]
22637    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
22638        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
22639        // `class MACRO(3, 6) Name final :` in the error, the first base as the
22640        // `type` of the following declaration, and closes that declaration with
22641        // a zero-width `MISSING identifier`. Reading the head by position must
22642        // not take that missing node for the class name: an empty name panics
22643        // at `FqName` construction, which took the Botan corpus replay down
22644        // (#2557). The head's tokens straddle the two nodes, so the positional
22645        // read spans both and recovers the class rather than leaving the
22646        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
22647        let source = r#"
22648namespace api {
22649class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
22650                                                    public virtual Botan::EC_PublicKey {
22651   public:
22652      std::string algo_name() const override { return "ECDSA"; }
22653};
22654} // namespace api
22655"#;
22656        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
22657        let declarations = parsed.declarations();
22658        assert!(
22659            declarations
22660                .iter()
22661                .all(|unit| !unit.identifier().is_empty()),
22662            "no declaration may carry an empty name: {declarations:#?}"
22663        );
22664        assert!(
22665            declarations
22666                .iter()
22667                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
22668            "macros and head specifiers must not become declarations: {declarations:#?}"
22669        );
22670        let class = declarations
22671            .iter()
22672            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
22673            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
22674        assert_eq!(
22675            parsed.raw_supertypes.get(class),
22676            Some(&vec![
22677                "Botan::TPM2::PublicKey".to_string(),
22678                "Botan::EC_PublicKey".to_string()
22679            ]),
22680            "both qualified virtual bases are bases: {declarations:#?}"
22681        );
22682        let source_facts = parsed.source_facts.as_ref().unwrap();
22683        let declaration = parsed
22684            .source_declaration_units
22685            .iter()
22686            .find(|(_, unit)| unit == class)
22687            .map(|(declaration, _)| *declaration)
22688            .expect("recovered class source bridge");
22689        let bases = source_facts
22690            .cpp
22691            .as_ref()
22692            .unwrap()
22693            .declarations
22694            .iter()
22695            .find(|fact| fact.declaration == declaration)
22696            .expect("recovered class source fact")
22697            .bases
22698            .clone();
22699        assert_eq!(
22700            bases
22701                .iter()
22702                .map(|base| base.components.clone())
22703                .collect::<Vec<_>>(),
22704            vec![
22705                vec!["Botan", "TPM2", "PublicKey"],
22706                vec!["Botan", "EC_PublicKey"],
22707            ],
22708            "generalized recovery must publish structured base components"
22709        );
22710        assert!(
22711            bases.iter().all(|base| base.is_virtual),
22712            "generalized recovery must preserve virtual base qualifiers"
22713        );
22714        assert!(
22715            declarations
22716                .iter()
22717                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
22718            "the head must not mint a macro-named class: {declarations:#?}"
22719        );
22720        let mixed_source = source.replace(
22721            "public virtual Botan::EC_PublicKey",
22722            "public Botan::EC_PublicKey",
22723        );
22724        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
22725        let qualifiers = mixed
22726            .source_facts
22727            .as_ref()
22728            .unwrap()
22729            .cpp
22730            .as_ref()
22731            .unwrap()
22732            .declarations
22733            .iter()
22734            .flat_map(|declaration| &declaration.bases)
22735            .map(|base| base.is_virtual)
22736            .collect::<Vec<_>>();
22737        assert_eq!(
22738            qualifiers,
22739            [true, false],
22740            "base qualifiers must not cross the comma"
22741        );
22742    }
22743
22744    #[test]
22745    #[hardwon(
22746        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22747    )]
22748    fn function_like_export_class_survives_a_preceding_malformed_body() {
22749        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
22750        let mut parser = tree_sitter::Parser::new();
22751        parser
22752            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22753            .expect("set C++ grammar");
22754        let tree = parser.parse(&source, None).expect("parse fixture");
22755        let mut stack = vec![tree.root_node()];
22756        let mut saw_embedded_shape = false;
22757        while let Some(node) = stack.pop() {
22758            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
22759                .iter()
22760                .any(|recovered| recovered.name == "Invalid_Argument");
22761            let mut cursor = node.walk();
22762            stack.extend(node.named_children(&mut cursor));
22763        }
22764        assert!(
22765            saw_embedded_shape,
22766            "fixture must retain the embedded error geometry: {}",
22767            tree.root_node().to_sexp()
22768        );
22769
22770        let parsed = parse_cpp_file(
22771            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
22772            &source,
22773            &tree,
22774        );
22775        let declarations = parsed.declarations();
22776        let exception = declarations
22777            .iter()
22778            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
22779            .expect("qualified-base export class");
22780        let invalid = declarations
22781            .iter()
22782            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
22783            .expect("class embedded in the preceding malformed body");
22784
22785        assert_eq!(
22786            parsed.raw_supertypes.get(exception),
22787            Some(&vec!["std::exception".to_string()])
22788        );
22789        assert_eq!(
22790            parsed.raw_supertypes.get(invalid),
22791            Some(&vec!["Exception".to_string()])
22792        );
22793        assert!(
22794            parsed.materialization_records.iter().any(|record| matches!(
22795                record,
22796                MaterializationRecord::RecoveredDeclaration { unit, .. }
22797                    if unit == invalid
22798            )),
22799            "the embedded class must retain recovery provenance: {:#?}",
22800            parsed.materialization_records
22801        );
22802    }
22803
22804    #[test]
22805    #[hardwon(
22806        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22807    )]
22808    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
22809        let source = r#"
22810public:
22811   explicit Lookup_Error(std::string_view err) : Exception(err) {}
22812
22813   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
22814"#;
22815        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
22816            .expect("reparse merged constructor body");
22817        let (range, body) =
22818            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22819                .unwrap_or_else(|| {
22820                    panic!(
22821                        "the merged constructor must retain its structured declarator/body: {}",
22822                        tree.root_node().to_sexp()
22823                    )
22824                });
22825        assert_eq!(
22826            source.get(range).expect("constructor range"),
22827            "Lookup_Error(std::string_view err) : Exception(err) {}"
22828        );
22829        assert_eq!(node_text(body, source), "{}");
22830    }
22831
22832    #[test]
22833    #[hardwon(
22834        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22835    )]
22836    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
22837        let positive_source =
22838            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
22839        let mut parser = tree_sitter::Parser::new();
22840        parser
22841            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22842            .unwrap();
22843        let positive_tree = parser.parse(positive_source, None).unwrap();
22844        assert!(cpp_reparsed_members_are_indexable(
22845            positive_tree.root_node(),
22846            positive_source
22847        ));
22848
22849        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
22850        let negative_tree = parser.parse(negative_source, None).unwrap();
22851        assert!(!cpp_reparsed_members_are_indexable(
22852            negative_tree.root_node(),
22853            negative_source
22854        ));
22855    }
22856
22857    #[test]
22858    #[hardwon(
22859        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22860    )]
22861    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
22862        let copy_control_source = r#"
22863public:
22864    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
22865    explicit Token(const Token* tok);
22866    ~Token();
22867    Token* astOperand1() { return nullptr; }
22868"#;
22869        let constraint_source = r#"
22870private:
22871    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
22872    static T *tokAtImpl(T *tok, int index) {
22873        return tok;
22874    }
22875
22876    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
22877    static T *linkAtImpl(T *tok, int index) {
22878        return tok;
22879    }
22880
22881public:
22882    int late() const { return 1; }
22883"#;
22884        let mut parser = tree_sitter::Parser::new();
22885        parser
22886            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22887            .unwrap();
22888        let copy_control_tree = parser
22889            .parse(copy_control_source, None)
22890            .expect("parse copy-control fixture");
22891        assert!(
22892            copy_control_tree.root_node().has_error(),
22893            "fixture must exercise adjacent copy-control recovery"
22894        );
22895        assert!(
22896            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
22897            "a complete late getter must remain recoverable after adjacent copy-control declarations"
22898        );
22899        let mut cursor = copy_control_tree.root_node().walk();
22900        assert!(
22901            copy_control_tree
22902                .root_node()
22903                .named_children(&mut cursor)
22904                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
22905            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
22906            copy_control_tree.root_node().to_sexp()
22907        );
22908        let constraint_tree = parser
22909            .parse(constraint_source, None)
22910            .expect("parse constraint-macro fixture");
22911        assert!(constraint_tree.root_node().has_error());
22912        assert!(
22913            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
22914            "complete constraint-macro members must not hide a later ordinary member"
22915        );
22916        let mut cursor = constraint_tree.root_node().walk();
22917        assert!(
22918            constraint_tree
22919                .root_node()
22920                .named_children(&mut cursor)
22921                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
22922                    child,
22923                    constraint_source
22924                )),
22925            "fixture must retain the split constraint-macro prefix/function geometry"
22926        );
22927    }
22928
22929    #[test]
22930    #[hardwon(
22931        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22932    )]
22933    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
22934        let source = r#"
22935struct Analyzer {
22936    struct Action {
22937        Action() = default;
22938        Action(const Action&) = default;
22939        Action& operator=(const Action& rhs) & = default;
22940
22941        template<class T,
22942                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
22943                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
22944        // NOLINTNEXTLINE(google-explicit-constructor)
22945        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
22946        {}
22947
22948        enum : std::uint16_t { None = 0, Read = (1 << 0) };
22949        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
22950
22951    private:
22952        unsigned int mFlag{};
22953    };
22954
22955    enum class Direction : unsigned char { Forward, Reverse };
22956    virtual Action analyze(Direction d) const = 0;
22957};
22958"#;
22959        let mut parser = tree_sitter::Parser::new();
22960        parser
22961            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22962            .unwrap();
22963        let tree = parser.parse(source, None).unwrap();
22964        assert!(tree.root_node().has_error());
22965        let root = tree.root_node();
22966        let outer = root
22967            .named_children(&mut root.walk())
22968            .find(|child| child.kind() == "ERROR")
22969            .expect("fragmented Analyzer prefix");
22970        let outer_recovered =
22971            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
22972        assert_eq!(outer_recovered.name, "Analyzer");
22973        let outer_tree = cpp_reparse_fragmented_class_body(
22974            source,
22975            outer_recovered.body.reparse_start,
22976            outer_recovered.body.reparse_end,
22977        )
22978        .expect("reparse Analyzer body");
22979        let outer_root = outer_tree.root_node();
22980        let action_prefix = outer_root
22981            .named_children(&mut outer_root.walk())
22982            .find(|child| child.kind() == "ERROR")
22983            .expect("fragmented Action prefix");
22984        let action_recovered = fragmented_class_body(action_prefix, source)
22985            .expect("structured Action fragment boundary");
22986        assert_eq!(action_recovered.name, "Action");
22987        let action_tree = cpp_reparse_fragmented_class_body(
22988            source,
22989            action_recovered.body.reparse_start,
22990            action_recovered.body.reparse_end,
22991        )
22992        .expect("reparse Action body");
22993        let action_root = action_tree.root_node();
22994        let macro_prefix = action_root
22995            .named_children(&mut action_root.walk())
22996            .find(|child| child.kind() == "ERROR")
22997            .expect("constraint macro prefix");
22998        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
22999            .expect("structured template macro prefix");
23000        let macro_companion =
23001            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
23002        assert!(
23003            cpp_reparsed_template_macro_constructor_companion_is_indexable(
23004                macro_companion,
23005                macro_parameter,
23006                source,
23007            ),
23008            "split constrained constructor must be admitted: {}",
23009            macro_companion.to_sexp()
23010        );
23011        assert!(
23012            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
23013            "complete Action body must pass the recovery gate: {}",
23014            action_tree.root_node().to_sexp()
23015        );
23016        assert!(
23017            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
23018            "complete Analyzer body must pass the recovery gate: {}",
23019            outer_tree.root_node().to_sexp()
23020        );
23021        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
23022        let parsed = parse_cpp_file(&file, source, &tree);
23023        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
23024            assert!(
23025                parsed
23026                    .declarations()
23027                    .iter()
23028                    .any(|unit| unit.fq_name() == expected),
23029                "missing recovered declaration {expected}: {:#?}",
23030                parsed.declarations()
23031            );
23032        }
23033        assert!(
23034            parsed
23035                .declarations()
23036                .iter()
23037                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
23038            "nested members must not remain flattened: {:#?}",
23039            parsed.declarations()
23040        );
23041    }
23042
23043    #[test]
23044    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
23045    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
23046        let source = "value() = default;\nint value();\n";
23047        let tree = crate::diagnostics::parse_cpp_tree(source);
23048        let root = tree.root_node();
23049        assert!(
23050            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
23051            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
23052            root.to_sexp()
23053        );
23054        assert!(
23055            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
23056            "a return-typed same-name declaration is not a constructor witness: {}",
23057            root.to_sexp()
23058        );
23059
23060        let damaged_source = r#"
23061value(int x) {
23062  broken(
23063}
23064value() = default;
23065int value();
23066"#;
23067        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
23068        let damaged_root = damaged_tree.root_node();
23069        let mut damaged_cursor = damaged_root.walk();
23070        let damaged_members = damaged_root
23071            .named_children(&mut damaged_cursor)
23072            .collect::<Vec<_>>();
23073        let damaged_assignment = damaged_members
23074            .iter()
23075            .copied()
23076            .find(|member| member.kind() == "expression_statement")
23077            .expect("damaged witness probe retains the default assignment expression");
23078        assert!(
23079            !cpp_reparsed_defaulted_constructor_is_indexable(
23080                damaged_assignment,
23081                &damaged_members,
23082                Some("value"),
23083                damaged_source,
23084            ),
23085            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
23086            damaged_root.to_sexp()
23087        );
23088    }
23089
23090    #[test]
23091    #[hardwon(
23092        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23093    )]
23094    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
23095        let source = r#"
23096raw_hash_set& operator=(raw_hash_set&& that) {
23097  return move_assign(
23098      std::move(that),
23099      typename AllocTraits::propagate_on_container_move_assignment());
23100}
23101
23102iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
23103  return {};
23104}
23105
23106void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
23107
23108iterator insert(const_iterator hint, value_type&& value)
23109    ABSL_ATTRIBUTE_LIFETIME_BOUND {
23110  return {};
23111}
23112
23113friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
23114  return left.size() == right.size();
23115}
23116
23117static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
23118  return static_cast<slot_type*>(buffer);
23119}
23120
23121protected:
23122// Included-range recovery can attach this comment to the template prefix.
23123template <class K>
23124void AssertOnFind([[maybe_unused]] const K& key) {
23125  Check(key);
23126}
23127"#;
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        assert!(
23134            tree.root_node().has_error(),
23135            "the fixture must exercise tree-sitter's errorful member shapes"
23136        );
23137        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
23138
23139        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
23140        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
23141        assert!(!cpp_reparsed_members_are_indexable(
23142            incomplete_tree.root_node(),
23143            incomplete_source
23144        ));
23145
23146        let outside_error_source = "int foo() stray_attribute {}\n";
23147        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
23148        assert!(outside_error_tree.root_node().has_error());
23149        assert!(!cpp_reparsed_members_are_indexable(
23150            outside_error_tree.root_node(),
23151            outside_error_source
23152        ));
23153
23154        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
23155        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
23156        assert!(!cpp_reparsed_members_are_indexable(
23157            variable_initializer_tree.root_node(),
23158            variable_initializer_source
23159        ));
23160    }
23161
23162    #[test]
23163    #[hardwon(
23164        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23165    )]
23166    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
23167        let positive_source = r#"
23168std::pair<iterator, bool> insert(init_type&& value)
23169    ABSL_ATTRIBUTE_LIFETIME_BOUND
23170#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
23171  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
23172#endif
23173{
23174  return emplace(std::move(value));
23175}
23176"#;
23177        let mut parser = tree_sitter::Parser::new();
23178        parser
23179            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23180            .unwrap();
23181        let positive_tree = parser.parse(positive_source, None).unwrap();
23182        assert!(
23183            positive_tree.root_node().has_error(),
23184            "the fixture must exercise the split attribute/requires shape"
23185        );
23186        assert!(cpp_reparsed_members_are_indexable(
23187            positive_tree.root_node(),
23188            positive_source
23189        ));
23190
23191        let template_return_source = r#"
23192pair<int> insert(init_type&& value)
23193    ABSL_ATTRIBUTE_LIFETIME_BOUND
23194#if LANGUAGE_LEVEL >= 202002L
23195  requires(!Predicate<init_type>::value)
23196#endif
23197// Attributes and the function body may be separated by comments.
23198{
23199  return {};
23200}
23201"#;
23202        let template_return_tree = parser.parse(template_return_source, None).unwrap();
23203        assert!(
23204            cpp_reparsed_members_are_indexable(
23205                template_return_tree.root_node(),
23206                template_return_source
23207            ),
23208            "template-return attribute/requires tree: {}",
23209            template_return_tree.root_node().to_sexp()
23210        );
23211
23212        let no_body_source = r#"
23213std::pair<iterator, bool> insert(init_type&& value)
23214    ABSL_ATTRIBUTE_LIFETIME_BOUND
23215#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
23216  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
23217#endif
23218+ 0;
23219"#;
23220        let no_body_tree = parser.parse(no_body_source, None).unwrap();
23221        assert!(!cpp_reparsed_members_are_indexable(
23222            no_body_tree.root_node(),
23223            no_body_source
23224        ));
23225
23226        let extra_payload_source = r#"
23227pair<int> insert(init_type&& value)
23228    ABSL_ATTRIBUTE_LIFETIME_BOUND
23229#if LANGUAGE_LEVEL >= 202002L
23230  int unrelated;
23231  requires(Predicate<init_type>::value)
23232#endif
23233{
23234  return {};
23235}
23236"#;
23237        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
23238        assert!(!cpp_reparsed_members_are_indexable(
23239            extra_payload_tree.root_node(),
23240            extra_payload_source
23241        ));
23242
23243        let variable_initializer_source = r#"
23244int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
23245#if LANGUAGE_LEVEL >= 202002L
23246  requires(true)
23247#endif
23248{
23249  bad;
23250}
23251"#;
23252        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
23253        assert!(!cpp_reparsed_members_are_indexable(
23254            variable_initializer_tree.root_node(),
23255            variable_initializer_source
23256        ));
23257    }
23258
23259    #[test]
23260    #[hardwon(
23261        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23262    )]
23263    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
23264        let source = r#"namespace absl {
23265ABSL_NAMESPACE_BEGIN namespace container_internal {
23266
23267class raw_hash_set : public Base {
23268 public:
23269  using value_type = int;
23270
23271  template <class U,
23272            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
23273  void insert(U value) { (void)value; }
23274
23275  struct InsertSlot {
23276    raw_hash_set& s;
23277  };
23278};
23279
23280}
23281ABSL_NAMESPACE_END
23282}"#;
23283        let mut parser = tree_sitter::Parser::new();
23284        parser
23285            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23286            .unwrap();
23287        let tree = parser.parse(source, None).unwrap();
23288        let root = tree.root_node();
23289        let outer_namespace = root
23290            .named_children(&mut root.walk())
23291            .find(|child| child.kind() == "namespace_definition")
23292            .expect("outer absl namespace");
23293        let declaration_list = outer_namespace
23294            .child_by_field_name("body")
23295            .expect("outer namespace body");
23296        let sentinel_function = declaration_list
23297            .named_children(&mut declaration_list.walk())
23298            .find(|child| child.kind() == "function_definition")
23299            .expect("malformed namespace sentinel function");
23300        let ancestry = ParentIndex::new(root);
23301        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
23302            .expect("structured nested namespace sentinel");
23303        let fragmented =
23304            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
23305                .expect("fragmented raw_hash_set class");
23306        assert_eq!(fragmented.class_node.kind(), "ERROR");
23307        assert_eq!(fragmented.name, "raw_hash_set");
23308        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
23309
23310        let outer_scope =
23311            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
23312        let mut outer_siblings = Vec::new();
23313        push_cpp_sentinel_sibling_classes(
23314            &mut outer_siblings,
23315            declaration_list,
23316            sentinel.function,
23317            &outer_scope,
23318            source,
23319            &ancestry,
23320        );
23321        let [outer_shadow] = outer_siblings.as_slice() else {
23322            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
23323        };
23324        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
23325        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
23326
23327        let field = "    raw_hash_set& s;";
23328        let start = source.find(field).expect("InsertSlot field") + 4;
23329        let node = root
23330            .descendant_for_byte_range(start, start + "raw_hash_set".len())
23331            .expect("raw_hash_set type node");
23332        let recovered = cpp_sentinel_recovered_classes(root, source);
23333        let [deep_class] = recovered.as_slice() else {
23334            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
23335        };
23336        assert_eq!(
23337            deep_class.namespace_scope_components,
23338            vec!["absl", "container_internal"]
23339        );
23340        assert_eq!(
23341            deep_class.scope_components,
23342            vec!["absl", "container_internal", "raw_hash_set"]
23343        );
23344        assert!(
23345            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
23346                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
23347        );
23348
23349        assert_eq!(
23350            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
23351            Some(vec![
23352                "absl".to_string(),
23353                "container_internal".to_string(),
23354                "raw_hash_set".to_string(),
23355                "InsertSlot".to_string(),
23356            ])
23357        );
23358
23359        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
23360        let parsed = parse_cpp_file(&file, source, &tree);
23361        let raw_hash_set = parsed
23362            .declarations()
23363            .iter()
23364            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
23365            .expect("recovered raw_hash_set class");
23366        assert_eq!(
23367            raw_hash_set.fq_name(),
23368            "absl::container_internal.raw_hash_set",
23369            "the recovered declaration must publish under the deeper sentinel namespace"
23370        );
23371        assert_eq!(
23372            parsed.raw_supertypes.get(raw_hash_set),
23373            Some(&vec!["Base".to_string()]),
23374            "the structured base clause on the fragmented ERROR prefix must survive publication"
23375        );
23376        assert!(
23377            parsed.materialization_records.iter().any(|record| matches!(
23378                record,
23379                MaterializationRecord::RecoveredDeclaration { recovery, unit }
23380                    if unit == raw_hash_set && *recovery == deep_class.class_range
23381            )),
23382            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
23383            parsed.materialization_records
23384        );
23385    }
23386
23387    /// Issue #2358: recording an aggregate definition must not walk the whole
23388    /// file.
23389    ///
23390    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
23391    /// class-like shape that has a body, so the removal step runs once per
23392    /// aggregate. It used to `retain` over `top_level_declarations` and over
23393    /// *every* child list in the file on each of those calls, comparing whole
23394    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
23395    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
23396    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
23397    /// tens of thousands of times over and the C forward differential never
23398    /// finished.
23399    ///
23400    /// A definition the file has not already declared removes nothing, so the
23401    /// honest cost is zero regardless of how many other aggregates surround it.
23402    /// Two sizes an order of magnitude apart pin that the count is not merely
23403    /// small but independent of the file.
23404    ///
23405    /// The declaration walk answers every ancestor question from a
23406    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
23407    /// the root for each one (#2361). Substituting the index is only safe
23408    /// because it answers the identical question, so pin that on the shapes
23409    /// this file's recovery paths care about: anonymous and named aggregates,
23410    /// nested namespaces, templates, macro-displaced declarations and the
23411    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
23412    /// too -- `Node::parent` walks the visible tree, not the named one.
23413    #[test]
23414    #[hardwon(
23415        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
23416    )]
23417    fn the_parent_index_answers_what_tree_sitter_answers() {
23418        const SHAPES: [&str; 5] = [
23419            "namespace outer { namespace inner { struct Tag { int field; }; } }",
23420            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
23421            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
23422            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
23423            "class API Broken : public First, public Second {\n  void member();\n",
23424        ];
23425        for source in SHAPES {
23426            let mut parser = tree_sitter::Parser::new();
23427            parser
23428                .set_language(&tree_sitter_cpp::LANGUAGE.into())
23429                .unwrap();
23430            let tree = parser.parse(source, None).unwrap();
23431            let root = tree.root_node();
23432            let ancestry = ParentIndex::new(root);
23433            let mut nodes = 0usize;
23434            let mut stack = vec![root];
23435            while let Some(node) = stack.pop() {
23436                nodes += 1;
23437                assert_eq!(
23438                    node.parent().map(|parent| parent.id()),
23439                    ancestry.parent(node).map(|parent| parent.id()),
23440                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
23441                );
23442                let mut cursor = node.walk();
23443                stack.extend(node.children(&mut cursor));
23444            }
23445            assert!(nodes > 1, "{source:?} produced no tree to compare");
23446        }
23447    }
23448
23449    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
23450    /// index for every ancestor edge. Asking tree-sitter directly makes each
23451    /// edge re-descend from the root, turning declaration extraction on a
23452    /// deeply nested generated header from quadratic output work into a cubic
23453    /// tree walk. Exact query counts pin the route without a machine-dependent
23454    /// wall-clock ceiling.
23455    #[test]
23456    #[hardwon(
23457        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
23458    )]
23459    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
23460        const DEPTH: usize = 64;
23461        let mut source = String::new();
23462        for level in 0..DEPTH {
23463            writeln!(source, "namespace n{level} {{").unwrap();
23464        }
23465        source.push_str("int deepest(int value);\n");
23466        for _ in 0..DEPTH {
23467            source.push_str("}\n");
23468        }
23469
23470        let mut parser = tree_sitter::Parser::new();
23471        parser
23472            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23473            .unwrap();
23474        let tree = parser.parse(&source, None).unwrap();
23475        let root = tree.root_node();
23476        let ancestry = ParentIndex::new(root);
23477        let mut function_declarator = None;
23478        walk_named_tree_preorder(root, true, |node| {
23479            if node.kind() == "function_declarator" {
23480                function_declarator = Some(node);
23481                WalkControl::Break
23482            } else {
23483                WalkControl::Continue
23484            }
23485        });
23486        let function_declarator = function_declarator.expect("deepest function declarator");
23487        let ancestor_count =
23488            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
23489
23490        ancestry.reset_parent_query_count_for_test();
23491        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
23492        assert_eq!(DEPTH, lexical_scope.len());
23493        assert_eq!(
23494            ancestor_count + 1,
23495            ancestry.parent_query_count_for_test(),
23496            "lexical-scope ancestry bypassed the parent index"
23497        );
23498
23499        ancestry.reset_parent_query_count_for_test();
23500        assert_eq!(
23501            Some(DispatchExtensibility::Closed),
23502            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
23503        );
23504        assert_eq!(
23505            ancestor_count,
23506            ancestry.parent_query_count_for_test(),
23507            "dispatch ancestry bypassed the parent index"
23508        );
23509
23510        ancestry.reset_parent_query_count_for_test();
23511        assert_eq!(
23512            CallableLinkage::External,
23513            cpp_callable_linkage(function_declarator, &source, &ancestry)
23514        );
23515        assert_eq!(
23516            ancestor_count + 1,
23517            ancestry.parent_query_count_for_test(),
23518            "linkage ancestry bypassed the parent index"
23519        );
23520
23521        ancestry.reset_parent_query_count_for_test();
23522        assert!(!cpp_callable_is_structural_constructor(
23523            function_declarator,
23524            &source,
23525            &ancestry
23526        ));
23527        assert_eq!(
23528            ancestor_count + 1,
23529            ancestry.parent_query_count_for_test(),
23530            "constructor ancestry bypassed the parent index"
23531        );
23532    }
23533
23534    /// #3507: the one row-level dispatch fact C++ can state is stated only
23535    /// where the language states it -- in the class body.
23536    ///
23537    /// A call's exactness is not readable from a definition row: an
23538    /// out-of-line definition repeats no `virtual`, no `final`, and no base
23539    /// clause, and C++ inherits virtual-ness from a base that declares the
23540    /// member. Each shape below therefore has to answer for itself, and only
23541    /// a class body with no base clause at all can close a member that writes
23542    /// nothing.
23543    #[test]
23544    #[hardwon(
23545        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
23546    )]
23547    fn callable_dispatch_extensibility_comes_from_the_class_body() {
23548        const SOURCE: &str = "\
23549class Plain { public: long send(long order); };
23550class Derived : public Base { public: long send(long order); };
23551class Sealed : public Base { public: long send(long order) final; };
23552class Hiding : public Base { public: long send(long order) { return order; } };
23553class Virtual { public: virtual long send(long order); };
23554namespace shop { long free_call(long order); }
23555long Plain::send(long order) { return order; }
23556";
23557        let mut parser = tree_sitter::Parser::new();
23558        parser
23559            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23560            .unwrap();
23561        let tree = parser.parse(SOURCE, None).unwrap();
23562        let root = tree.root_node();
23563        let ancestry = ParentIndex::new(root);
23564        let mut answers = Vec::new();
23565        walk_named_tree_preorder(root, true, |node| {
23566            if node.kind() != "function_declarator" {
23567                return WalkControl::Continue;
23568            }
23569            let name = cpp_function_declarator_name_node(node).expect("declarator name");
23570            answers.push((
23571                node_text(name, SOURCE).to_string(),
23572                cpp_callable_dispatch_extensibility(node, &ancestry),
23573            ));
23574            WalkControl::Continue
23575        });
23576        assert_eq!(
23577            answers,
23578            vec![
23579                ("send".to_string(), Some(DispatchExtensibility::Closed)),
23580                ("send".to_string(), Some(DispatchExtensibility::Open)),
23581                ("send".to_string(), Some(DispatchExtensibility::Closed)),
23582                ("send".to_string(), Some(DispatchExtensibility::Open)),
23583                ("send".to_string(), Some(DispatchExtensibility::Open)),
23584                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
23585                ("Plain::send".to_string(), None),
23586            ],
23587            "the class body states the fact; a qualified definition states none"
23588        );
23589    }
23590
23591    #[test]
23592    #[hardwon(
23593        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
23594    )]
23595    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
23596        const DISTINCT_PER_KIND: usize = 64;
23597        let mut source = String::new();
23598        for index in 0..DISTINCT_PER_KIND {
23599            writeln!(source, "typedef int Alias{index};").unwrap();
23600        }
23601        writeln!(source, "typedef long Alias0;").unwrap();
23602        for index in 0..DISTINCT_PER_KIND {
23603            writeln!(source, "#define MACRO_{index} {index}").unwrap();
23604        }
23605        writeln!(source, "#define MACRO_0 duplicate").unwrap();
23606        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
23607
23608        let mut parser = tree_sitter::Parser::new();
23609        parser
23610            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23611            .unwrap();
23612        let tree = parser.parse(&source, None).unwrap();
23613        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
23614
23615        start_declaration_identity_comparison_probe();
23616        let parsed = parse_cpp_file(&file, &source, &tree);
23617        let comparisons = finish_declaration_identity_comparison_probe();
23618
23619        assert_eq!(
23620            DISTINCT_PER_KIND + 1,
23621            parsed
23622                .declarations()
23623                .iter()
23624                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
23625                .count(),
23626            "every physical typedef alias declaration must be retained so \
23627             conditional branch guards stay available to the resolver"
23628        );
23629        assert_eq!(
23630            DISTINCT_PER_KIND + 1,
23631            parsed
23632                .declarations()
23633                .iter()
23634                .filter(|unit| {
23635                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
23636                })
23637                .count(),
23638            "distinct macro redefinitions must remain available to temporal lookup"
23639        );
23640        assert_eq!(
23641            2,
23642            parsed
23643                .declarations()
23644                .iter()
23645                .filter(|unit| {
23646                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
23647                })
23648                .count(),
23649            "function overloads must remain distinct"
23650        );
23651
23652        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
23653        assert!(
23654            comparisons <= dedup_inputs * 4,
23655            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
23656        );
23657    }
23658
23659    #[test]
23660    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
23661    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
23662        let negative_source = r#"namespace absl {
23663ABSL_NAMESPACE_BEGIN namespace container_internal {
23664template <typename T>
23665class broken {
23666 public:
23667  using value_type = T;
23668  T operator->() const { return &operator*(); }
23669}
23670}
23671"#;
23672        let mut parser = tree_sitter::Parser::new();
23673        parser
23674            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23675            .unwrap();
23676        let negative_tree = parser.parse(negative_source, None).unwrap();
23677        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
23678            .expect("the negative fixture must expose the malformed class node");
23679        assert!(
23680            broken.has_error(),
23681            "the negative fixture must retain a parser error"
23682        );
23683        assert!(
23684            cpp_complete_class_body_close(broken).is_none(),
23685            "the malformed class must not expose a real body close"
23686        );
23687        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
23688        assert!(
23689            recovered
23690                .iter()
23691                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
23692            "an incomplete class must not borrow the namespace close: {recovered:#?}"
23693        );
23694
23695        let positive_source = r#"namespace absl {
23696ABSL_NAMESPACE_BEGIN namespace container_internal {
23697template <typename T>
23698class broken {
23699 public:
23700  using value_type = T;
23701  T operator->() const { return &operator*(); }
23702  using alias = value_type;
23703};
23704}
23705}
23706"#;
23707        let positive_tree = parser.parse(positive_source, None).unwrap();
23708        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
23709            .expect("the positive fixture must expose the broken class node");
23710        assert!(
23711            broken.has_error(),
23712            "the positive fixture must retain an internal parser error"
23713        );
23714        assert!(
23715            cpp_complete_class_body_close(broken).is_some(),
23716            "the positive fixture must expose a real class body close"
23717        );
23718        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
23719        assert!(
23720            recovered.iter().any(|class| {
23721                class.scope_components == ["absl", "container_internal", "broken"]
23722            }),
23723            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
23724        );
23725    }
23726
23727    #[test]
23728    #[hardwon(
23729        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
23730    )]
23731    fn sentinel_recovery_keeps_members_after_nested_body_close() {
23732        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
23733NLOHMANN_BASIC_JSON_TPL_DECLARATION
23734class basic_json {
23735 private:
23736  union storage {
23737    int value;
23738  } data;
23739 public:
23740  using late_alias = int;
23741  late_alias value() const;
23742};
23743NLOHMANN_JSON_NAMESPACE_END
23744"#;
23745        let mut parser = tree_sitter::Parser::new();
23746        parser
23747            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23748            .unwrap();
23749        let tree = parser.parse(source, None).unwrap();
23750        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23751        let basic_json = recovered
23752            .iter()
23753            .find(|class| {
23754                class
23755                    .scope_components
23756                    .last()
23757                    .is_some_and(|name| name == "basic_json")
23758            })
23759            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
23760        let late_alias = source
23761            .find("late_alias value")
23762            .expect("late alias reference");
23763        assert!(
23764            basic_json.class_range.start_byte < late_alias
23765                && late_alias < basic_json.class_range.end_byte,
23766            "the recovered class range must include members after a nested close: {basic_json:#?}"
23767        );
23768    }
23769
23770    #[test]
23771    #[hardwon(
23772        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
23773    )]
23774    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
23775        let source = r#"namespace absl {
23776ABSL_NAMESPACE_BEGIN namespace container_internal {
23777template <typename T>
23778struct broken {
23779  using value_type = T;
23780};
23781}
23782
23783#ifdef OWNER_DEF
23784template <typename T>
23785typename broken<T>::value_type broken<T>::method() {
23786  value_type value{};
23787  return value;
23788}
23789#endif
23790
23791namespace sibling {
23792template <typename T>
23793typename broken<T>::value_type broken<T>::other() {
23794  value_type value{};
23795  return value;
23796}
23797}
23798
23799ABSL_NAMESPACE_END
23800}
23801"#;
23802        let mut parser = tree_sitter::Parser::new();
23803        parser
23804            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23805            .unwrap();
23806        let tree = parser.parse(source, None).unwrap();
23807        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23808        let broken = recovered
23809            .iter()
23810            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
23811            .expect("the sentinel class must be recovered");
23812        let method_start = source
23813            .find("typename broken<T>::value_type broken<T>::method()")
23814            .expect("guarded sibling owner");
23815        let method_end = source[method_start..]
23816            .find("\n}")
23817            .map(|offset| method_start + offset + 2)
23818            .expect("guarded sibling owner close");
23819        assert!(
23820            broken
23821                .owner_ranges
23822                .iter()
23823                .any(|owner| owner.range.start_byte <= method_start
23824                    && method_end <= owner.range.end_byte),
23825            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
23826        );
23827        let sibling_start = source
23828            .find("typename broken<T>::value_type broken<T>::other()")
23829            .expect("nested namespace sibling owner");
23830        assert!(
23831            broken
23832                .owner_ranges
23833                .iter()
23834                .all(|owner| owner.range.start_byte > sibling_start
23835                    || owner.range.end_byte <= sibling_start),
23836            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
23837        );
23838    }
23839
23840    #[test]
23841    #[hardwon(
23842        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
23843    )]
23844    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
23845        let source = r#"#ifdef OUTER
23846namespace absl {
23847ABSL_NAMESPACE_BEGIN namespace container_internal {
23848template <typename T>
23849struct broken {
23850  using value_type = T;
23851};
23852}
23853}
23854
23855#ifdef OWNER_DEF
23856template <typename T>
23857typename broken<T>::value_type broken<T>::method() {
23858  value_type value{};
23859  return value;
23860}
23861#endif
23862#endif
23863"#;
23864        let mut parser = tree_sitter::Parser::new();
23865        parser
23866            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23867            .unwrap();
23868        let tree = parser.parse(source, None).unwrap();
23869        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
23870        let broken = recovered
23871            .iter()
23872            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
23873            .expect("the sentinel class must be recovered");
23874        let method_start = source
23875            .find("typename broken<T>::value_type broken<T>::method()")
23876            .expect("outer sibling owner");
23877        assert!(
23878            broken
23879                .owner_ranges
23880                .iter()
23881                .all(|owner| owner.range.start_byte > method_start
23882                    || owner.range.end_byte <= method_start),
23883            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
23884        );
23885    }
23886
23887    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
23888    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
23889        let mut signatures = all_function_identities(parsed)
23890            .into_iter()
23891            .filter(|(name, _)| name == fq_name)
23892            .filter_map(|(_, signature)| signature)
23893            .collect::<Vec<_>>();
23894        signatures.sort();
23895        signatures.dedup();
23896        signatures
23897    }
23898
23899    #[test]
23900    #[hardwon(
23901        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
23902    )]
23903    fn callable_parameter_types_come_from_the_ast_parameter_list() {
23904        let source = r#"
23905template <typename T, ENABLE_BYTES(T)>
23906Vec256<T> DupOdd(Vec256<T> value) { return value; }
23907
23908struct Visitor {
23909  void fail(this auto const& self) {}
23910  void consume(this Visitor const& self, int count) {}
23911};
23912"#;
23913        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
23914        let dup_odd = parsed
23915            .declarations()
23916            .iter()
23917            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
23918            .expect("DupOdd declaration");
23919        assert_eq!(
23920            dup_odd.signature(),
23921            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
23922        );
23923        assert_eq!(
23924            parsed
23925                .signature_metadata
23926                .get(dup_odd)
23927                .and_then(|metadata| metadata.first())
23928                .and_then(SignatureMetadata::callable_parameter_types),
23929            Some(["Vec256<T>".to_string()].as_slice())
23930        );
23931
23932        let fail = parsed
23933            .declarations()
23934            .iter()
23935            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
23936            .expect("explicit-object member");
23937        assert_eq!(fail.signature(), Some("(this const auto &)"));
23938        let metadata = parsed
23939            .signature_metadata
23940            .get(fail)
23941            .and_then(|metadata| metadata.first())
23942            .expect("explicit-object signature metadata");
23943        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
23944        assert!(
23945            metadata
23946                .callable_arity()
23947                .is_some_and(|arity| arity.accepts(0))
23948        );
23949        let consume = parsed
23950            .declarations()
23951            .iter()
23952            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
23953            .expect("explicit-object member with an invocation argument");
23954        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
23955        let metadata = parsed
23956            .signature_metadata
23957            .get(consume)
23958            .and_then(|metadata| metadata.first())
23959            .expect("typed explicit-object signature metadata");
23960        assert_eq!(
23961            metadata.callable_parameter_types(),
23962            Some(["int".to_string()].as_slice())
23963        );
23964        assert!(
23965            metadata
23966                .callable_arity()
23967                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
23968        );
23969    }
23970
23971    #[test]
23972    #[hardwon(
23973        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
23974    )]
23975    fn trailing_qualifiers_survive_parameter_list_whitespace() {
23976        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
23977        // declarator's structure, so an out-of-line definition that spells its
23978        // parameter list with different whitespace than the declaration must
23979        // still carry it.
23980        let source = r#"
23981struct Widget {
23982  bool multiline(int settings, int supprs) const;
23983  bool doublespace(int settings, int supprs) const;
23984  bool noexcept_multiline(int settings, int supprs) noexcept;
23985  bool ref_multiline(int settings, int supprs) &&;
23986};
23987bool
23988Widget::multiline (int settings,
23989                   int supprs) const
23990{ return settings + supprs > 0; }
23991bool Widget::doublespace(int settings,  int supprs) const { return true; }
23992bool Widget::noexcept_multiline(int settings,
23993                                int supprs) noexcept { return true; }
23994bool Widget::ref_multiline(int settings,
23995                           int supprs) && { return true; }
23996"#;
23997        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
23998        assert_eq!(
23999            vec!["(int, int) const".to_string()],
24000            identity_signatures(&parsed, "Widget.multiline")
24001        );
24002        assert_eq!(
24003            vec!["(int, int) const".to_string()],
24004            identity_signatures(&parsed, "Widget.doublespace")
24005        );
24006        assert_eq!(
24007            vec!["(int, int) noexcept".to_string()],
24008            identity_signatures(&parsed, "Widget.noexcept_multiline")
24009        );
24010        assert_eq!(
24011            vec!["(int, int) &&".to_string()],
24012            identity_signatures(&parsed, "Widget.ref_multiline")
24013        );
24014    }
24015
24016    #[test]
24017    #[hardwon(
24018        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24019    )]
24020    fn macro_fragmented_plain_class_keeps_following_member_signature() {
24021        let source = r#"
24022struct CString {};
24023class CMessage {
24024public:
24025  CString GetParams(unsigned int index, unsigned int length = -1) const
24026      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
24027    return GetParamsColon(index, length);
24028  }
24029  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
24030};
24031CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
24032  return {};
24033}
24034"#;
24035        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
24036        assert_eq!(
24037            vec!["(unsigned int, unsigned int) const".to_string()],
24038            identity_signatures(&parsed, "CMessage.GetParamsColon")
24039        );
24040    }
24041
24042    #[test]
24043    #[hardwon(
24044        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
24045    )]
24046    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
24047        let source = r#"
24048#pragma once
24049#define DEMO_DEPRECATED(message)
24050namespace demo {
24051struct Base {
24052    static int aligned(int value) { return value; }
24053    int legacy(int value) const
24054        DEMO_DEPRECATED("use replacement()") { return value; }
24055    int replacement() const;
24056    void run(int value);
24057};
24058struct OtherBase {
24059    void run(int value);
24060    static int aligned(int value) { return value; }
24061};
24062struct Derived : Base {};
24063struct Override : Base {
24064    void run(int value);
24065    static int aligned(int value) { return value; }
24066};
24067struct RecoveredOverride : Base {
24068    int legacy(int value) const
24069        DEMO_DEPRECATED("use replacement()") { return value; }
24070    void run(int value);
24071};
24072struct Hidden : Base {
24073    void run(int first, int second);
24074    static int aligned(int first, int second) { return first + second; }
24075};
24076struct Ambiguous : Base, OtherBase {};
24077}
24078struct Global {};
24079"#;
24080        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
24081        let declarations = parsed.declarations();
24082        let fq_names = declarations
24083            .iter()
24084            .map(|unit| unit.fq_name())
24085            .collect::<std::collections::BTreeSet<_>>();
24086
24087        for expected in [
24088            "demo.Base",
24089            "demo.Base.aligned",
24090            "demo.Base.legacy",
24091            "demo.Base.replacement",
24092            "demo.Base.run",
24093            "demo.Derived",
24094            "demo.OtherBase",
24095            "demo.Override",
24096            "demo.RecoveredOverride",
24097            "demo.Hidden",
24098            "demo.Ambiguous",
24099            "Global",
24100        ] {
24101            assert!(
24102                fq_names.contains(expected),
24103                "missing {expected} from namespaced macro fragment: {declarations:#?}"
24104            );
24105        }
24106        assert!(
24107            !fq_names.contains("Derived"),
24108            "following class escaped its namespace: {declarations:#?}"
24109        );
24110        assert!(
24111            !fq_names.contains("demo.Global"),
24112            "global class crossed the recovered namespace boundary: {declarations:#?}"
24113        );
24114    }
24115
24116    #[test]
24117    #[hardwon(
24118        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
24119    )]
24120    fn trailing_qualifiers_still_separate_genuine_overloads() {
24121        // The qualifier must keep distinguishing the real C++ overload sets it
24122        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
24123        let source = r#"
24124struct Widget {
24125  int* slot(int index);
24126  const int* slot(int index) const;
24127  int log(int severity) &;
24128  int log(int severity) &&;
24129};
24130"#;
24131        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
24132        assert_eq!(
24133            vec!["(int)".to_string(), "(int) const".to_string()],
24134            identity_signatures(&parsed, "Widget.slot")
24135        );
24136        assert_eq!(
24137            vec!["(int) &".to_string(), "(int) &&".to_string()],
24138            identity_signatures(&parsed, "Widget.log")
24139        );
24140    }
24141
24142    #[test]
24143    #[hardwon(
24144        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
24145    )]
24146    fn virtual_specifier_is_not_part_of_the_identity_signature() {
24147        // `override` never appears on the out-of-line definition, and C++ does
24148        // not make it part of the signature, so it must not split the identity.
24149        let source = r#"
24150struct Base {
24151  virtual void run(int value) const;
24152};
24153struct Widget : Base {
24154  void run(int value) const override;
24155};
24156void Widget::run(int value) const {}
24157"#;
24158        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
24159        assert_eq!(
24160            vec!["(int) const".to_string()],
24161            identity_signatures(&parsed, "Widget.run")
24162        );
24163    }
24164
24165    #[test]
24166    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
24167    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
24168        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
24169        // the function type, so a declaration that spells `const int` and a
24170        // definition that spells `int` are one entity.
24171        let source = r#"
24172struct Widget {
24173  bool value_params(const int settings, const int supprs);
24174  void pointee_const(const int* p);
24175  void pointer_const(int* const p);
24176  void both_const(const int* const p);
24177  void reference_const(const int& p);
24178  void array_const(const int values[4]);
24179  void take(const int* p);
24180  void take(int* p);
24181};
24182bool Widget::value_params(int settings, int supprs) { return true; }
24183void Widget::pointer_const(int* p) {}
24184void Widget::both_const(const int* p) {}
24185"#;
24186        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
24187        assert_eq!(
24188            vec!["(int, int)".to_string()],
24189            identity_signatures(&parsed, "Widget.value_params")
24190        );
24191        assert_eq!(
24192            vec!["(int *)".to_string()],
24193            identity_signatures(&parsed, "Widget.pointer_const")
24194        );
24195        assert_eq!(
24196            vec!["(const int *)".to_string()],
24197            identity_signatures(&parsed, "Widget.both_const")
24198        );
24199        // The const that is not top-level still distinguishes the type.
24200        assert_eq!(
24201            vec!["(const int *)".to_string()],
24202            identity_signatures(&parsed, "Widget.pointee_const")
24203        );
24204        assert_eq!(
24205            vec!["(const int &)".to_string()],
24206            identity_signatures(&parsed, "Widget.reference_const")
24207        );
24208        assert_eq!(
24209            vec!["(const int [4])".to_string()],
24210            identity_signatures(&parsed, "Widget.array_const")
24211        );
24212        assert_eq!(
24213            vec!["(const int *)".to_string(), "(int *)".to_string()],
24214            identity_signatures(&parsed, "Widget.take")
24215        );
24216    }
24217
24218    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
24219        let mut parser = tree_sitter::Parser::new();
24220        parser
24221            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24222            .unwrap();
24223        let tree = parser.parse(source, None).unwrap();
24224        let start = source.find(callable_name).expect("callable declaration");
24225        let declarator =
24226            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
24227        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
24228    }
24229
24230    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
24231        let mut shapes = comparable_shapes(source, callable_name);
24232        assert_eq!(1, shapes.len(), "{shapes:?}");
24233        match shapes.remove(0) {
24234            CppComparableSlot::Shape(shape) => shape,
24235            other => panic!("expected a comparable shape, got {other:?}"),
24236        }
24237    }
24238
24239    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
24240        let mut current = shape.root();
24241        loop {
24242            match shape.node(current) {
24243                CppComparableNode::Named { .. } => return shape.node(current),
24244                CppComparableNode::Pointer { inner, .. }
24245                | CppComparableNode::Reference { inner }
24246                | CppComparableNode::Array { inner } => current = *inner,
24247                CppComparableNode::Generic { base, .. } => current = *base,
24248            }
24249        }
24250    }
24251
24252    #[test]
24253    #[hardwon(
24254        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
24255    )]
24256    fn comparable_shape_keeps_pointee_const() {
24257        assert_ne!(
24258            sole_comparable_shape("void f(const char* p);", "f("),
24259            sole_comparable_shape("void f(char* p);", "f(")
24260        );
24261    }
24262
24263    #[test]
24264    #[hardwon(
24265        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
24266    )]
24267    fn comparable_shape_keeps_inner_pointer_const() {
24268        assert_ne!(
24269            sole_comparable_shape("void f(int** p);", "f("),
24270            sole_comparable_shape("void f(int* const* p);", "f(")
24271        );
24272    }
24273
24274    #[test]
24275    #[hardwon(
24276        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
24277    )]
24278    fn comparable_shape_drops_top_level_pointer_const() {
24279        assert_eq!(
24280            sole_comparable_shape("void f(int* const p);", "f("),
24281            sole_comparable_shape("void f(int* p);", "f(")
24282        );
24283    }
24284
24285    #[test]
24286    #[hardwon(
24287        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
24288    )]
24289    fn comparable_shape_drops_top_level_base_const() {
24290        assert_eq!(
24291            sole_comparable_shape("void f(const int p);", "f("),
24292            sole_comparable_shape("void f(int p);", "f(")
24293        );
24294    }
24295
24296    #[test]
24297    #[hardwon(
24298        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
24299    )]
24300    fn comparable_shape_decays_top_level_array_to_pointer() {
24301        assert_eq!(
24302            sole_comparable_shape("void f(int a[3]);", "f("),
24303            sole_comparable_shape("void f(int* a);", "f(")
24304        );
24305        assert_eq!(
24306            sole_comparable_shape("void f(int* a[3]);", "f("),
24307            sole_comparable_shape("void f(int** a);", "f(")
24308        );
24309    }
24310
24311    #[test]
24312    #[hardwon(
24313        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
24314    )]
24315    fn comparable_shape_keeps_array_behind_pointer() {
24316        assert_ne!(
24317            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
24318            sole_comparable_shape("struct S { void f(int** a); };", "f(")
24319        );
24320    }
24321
24322    #[test]
24323    #[hardwon(
24324        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
24325    )]
24326    fn comparable_shape_records_written_name_and_lexical_scope() {
24327        let declared =
24328            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
24329        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
24330        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
24331            panic!("named leaf");
24332        };
24333        assert_eq!(["Msg".to_string()].as_slice(), name.path());
24334        assert_eq!(
24335            ["ns".to_string(), "S".to_string()].as_slice(),
24336            name.lexical_scope()
24337        );
24338        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
24339            panic!("named leaf");
24340        };
24341        assert_eq!(
24342            ["ns".to_string(), "Msg".to_string()].as_slice(),
24343            name.path()
24344        );
24345        assert!(name.lexical_scope().is_empty());
24346        assert_ne!(declared, defined);
24347    }
24348
24349    #[test]
24350    #[hardwon(
24351        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
24352    )]
24353    fn comparable_shape_marks_sized_primitive_leaf() {
24354        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
24355        let CppComparableNode::Named {
24356            name, primitive, ..
24357        } = comparable_named_leaf(&shape)
24358        else {
24359            panic!("named leaf");
24360        };
24361        assert!(primitive);
24362        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
24363        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
24364    }
24365
24366    #[test]
24367    #[hardwon(
24368        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
24369    )]
24370    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
24371        assert_eq!(
24372            vec![CppComparableSlot::Unstructured],
24373            comparable_shapes("void f(void (*cb)(int));", "f(")
24374        );
24375    }
24376
24377    #[test]
24378    #[hardwon(
24379        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
24380    )]
24381    fn comparable_shape_reports_ellipsis_slot() {
24382        let shapes = comparable_shapes("void f(int a, ...);", "f(");
24383        assert_eq!(2, shapes.len(), "{shapes:?}");
24384        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
24385    }
24386
24387    #[test]
24388    #[hardwon(
24389        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
24390    )]
24391    fn comparable_shape_keeps_template_argument_const() {
24392        assert_ne!(
24393            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
24394            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
24395        );
24396    }
24397
24398    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
24399    /// file-scope tag that a later `struct inner *` at file scope may name.
24400    #[test]
24401    #[hardwon(
24402        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
24403    )]
24404    fn c_file_mints_aggregate_member_tag_at_file_scope() {
24405        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
24406        let parsed = parse_cpp_declarations(source, "x.c");
24407        let declarations = parsed.declarations();
24408
24409        assert!(
24410            declarations
24411                .iter()
24412                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
24413            "expected a file-scope inner tag, got {declarations:?}"
24414        );
24415        assert!(
24416            declarations
24417                .iter()
24418                .all(|unit| unit.fq_name() != "outer$inner"),
24419            "expected no nested identity, got {declarations:?}"
24420        );
24421        assert!(
24422            declarations
24423                .iter()
24424                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
24425        );
24426        // Members still belong to their own aggregate.
24427        assert!(
24428            declarations
24429                .iter()
24430                .any(|unit| unit.fq_name() == "inner.value")
24431        );
24432        assert!(
24433            declarations
24434                .iter()
24435                .any(|unit| unit.fq_name() == "outer.item")
24436        );
24437
24438        let outer = declarations
24439            .iter()
24440            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
24441            .expect("outer");
24442        assert!(
24443            parsed
24444                .children
24445                .get(outer)
24446                .into_iter()
24447                .flatten()
24448                .all(|child| child.fq_name() != "inner"),
24449            "the tag must not hang off the aggregate it is written inside: {:?}",
24450            parsed.children
24451        );
24452    }
24453
24454    /// A header carries no compilation language of its own, and a `.cpp`
24455    /// translation unit really does declare a nested class. Both keep exactly
24456    /// the C++ extraction they had before the C dialect existed.
24457    #[test]
24458    #[hardwon(
24459        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
24460    )]
24461    fn header_and_cpp_files_keep_nested_tag_identity() {
24462        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
24463        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
24464            let parsed = parse_cpp_declarations(source, name);
24465            let declarations = parsed.declarations();
24466            assert!(
24467                declarations
24468                    .iter()
24469                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
24470                "{name} must keep the nested identity, got {declarations:?}"
24471            );
24472            assert!(
24473                declarations.iter().all(|unit| unit.fq_name() != "inner"),
24474                "{name} must not mint a file-scope tag, got {declarations:?}"
24475            );
24476            assert!(
24477                declarations
24478                    .iter()
24479                    .any(|unit| unit.fq_name() == "outer$inner.value")
24480            );
24481        }
24482    }
24483
24484    #[test]
24485    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
24486        let source = r#"
24487template <class K, class V, class Traits = CacheTraits<K>>
24488class ARCCache {
24489 public:
24490    struct ArcSizeInfo {
24491        void BSize() {}
24492    };
24493 private:
24494    struct BMapVal;
24495    struct BListVal;
24496    typedef std::unordered_map<K, BMapVal> BMap;
24497    typedef typename BMap::iterator bmap_iter;
24498    typedef typename std::list<BListVal> BList;
24499    typedef typename BList::iterator blist_iter;
24500    struct BListVal {
24501        bmap_iter map_iter;
24502        BListVal() = default;
24503    };
24504    struct DefinedVal {
24505        DefinedVal() {}
24506    };
24507};
24508"#;
24509        let tree = crate::diagnostics::parse_cpp_tree(source);
24510        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
24511        let cpp = crate::adapter::parse_cpp_file_in_dialect(
24512            &file,
24513            source,
24514            &tree,
24515            LanguageDialect::Standard(Language::Cpp),
24516        );
24517        let c =
24518            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
24519        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
24520            for fq_name in [
24521                "ARCCache$ArcSizeInfo.BSize",
24522                "ARCCache$BListVal.BListVal",
24523                "ARCCache$DefinedVal.DefinedVal",
24524            ] {
24525                assert!(
24526                    parsed
24527                        .declarations()
24528                        .iter()
24529                        .any(|unit| unit.fq_name() == fq_name),
24530                    "{dialect} reading must contain {fq_name}: {:#?}",
24531                    parsed.declarations()
24532                );
24533            }
24534            for flattened in [
24535                "ArcSizeInfo.BSize",
24536                "BListVal.BListVal",
24537                "DefinedVal.DefinedVal",
24538            ] {
24539                assert!(
24540                    parsed
24541                        .declarations()
24542                        .iter()
24543                        .all(|unit| unit.fq_name() != flattened),
24544                    "{dialect} reading must not flatten {flattened}: {:#?}",
24545                    parsed.declarations()
24546                );
24547            }
24548        }
24549    }
24550
24551    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
24552    #[test]
24553    #[hardwon(
24554        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
24555    )]
24556    fn uppercase_c_extension_keeps_cpp_tag_scope() {
24557        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
24558        let parsed = parse_cpp_declarations(source, "x.C");
24559        assert!(
24560            parsed
24561                .declarations()
24562                .iter()
24563                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
24564        );
24565    }
24566
24567    /// There is no such thing as a partially nested tag in C: every level of a
24568    /// nested aggregate chain lands at the same enclosing scope.
24569    #[test]
24570    #[hardwon(
24571        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
24572    )]
24573    fn c_file_mints_every_nesting_level_at_file_scope() {
24574        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
24575        let parsed = parse_cpp_declarations(source, "z.c");
24576        let declarations = parsed.declarations();
24577
24578        for tag in ["a", "b", "c"] {
24579            assert!(
24580                declarations
24581                    .iter()
24582                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
24583                "expected a file-scope {tag}, got {declarations:?}"
24584            );
24585        }
24586        assert!(
24587            declarations
24588                .iter()
24589                .all(|unit| !unit.fq_name().contains('$')),
24590            "no level may keep a nested identity, got {declarations:?}"
24591        );
24592        // Each member still belongs to the aggregate that declares it.
24593        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
24594        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
24595        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
24596    }
24597
24598    /// An enum tag is a tag; its enumerators stay members of the enum, which is
24599    /// what makes them ordinary identifiers at the enum's own (file) scope.
24600    #[test]
24601    #[hardwon(
24602        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
24603    )]
24604    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
24605        let source = "struct outer { enum color { RED, GREEN } c; };\n";
24606        let parsed = parse_cpp_declarations(source, "e.c");
24607        let declarations = parsed.declarations();
24608
24609        let color = declarations
24610            .iter()
24611            .find(|unit| unit.is_class() && unit.fq_name() == "color")
24612            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
24613        assert!(
24614            declarations
24615                .iter()
24616                .all(|unit| unit.fq_name() != "outer$color")
24617        );
24618        for enumerator in ["color.RED", "color.GREEN"] {
24619            assert!(
24620                declarations.iter().any(|unit| unit.fq_name() == enumerator),
24621                "expected {enumerator}, got {declarations:?}"
24622            );
24623        }
24624        let children = parsed
24625            .children
24626            .get(color)
24627            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
24628        assert!(
24629            ["color.RED", "color.GREEN"]
24630                .iter()
24631                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
24632            "enumerators must hang off their enum: {children:?}"
24633        );
24634    }
24635
24636    #[test]
24637    #[hardwon(
24638        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
24639    )]
24640    fn c_file_mints_member_list_union_at_file_scope() {
24641        let source = "struct outer { union inner { int a; float b; } item; };\n";
24642        let parsed = parse_cpp_declarations(source, "u.c");
24643        let declarations = parsed.declarations();
24644        assert!(
24645            declarations
24646                .iter()
24647                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
24648            "expected a file-scope inner union, got {declarations:?}"
24649        );
24650        assert!(
24651            declarations
24652                .iter()
24653                .all(|unit| unit.fq_name() != "outer$inner")
24654        );
24655        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
24656        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
24657    }
24658
24659    /// A tag declared in a namespace member list is not a file-scope tag: the
24660    /// nearest enclosing non-aggregate scope is the namespace.
24661    #[test]
24662    #[hardwon(
24663        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
24664    )]
24665    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
24666        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
24667        let parsed = parse_cpp_declarations(source, "n.c");
24668        let declarations = parsed.declarations();
24669        let inner = declarations
24670            .iter()
24671            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
24672            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
24673        assert_eq!(inner.package_name(), "ns");
24674        assert!(
24675            declarations
24676                .iter()
24677                .all(|unit| unit.fq_name() != "ns.outer$inner")
24678        );
24679    }
24680
24681    /// Pins today's treatment of a tag declared inside a function body: the
24682    /// declaration walk does not descend into statement bodies, so no unit is
24683    /// minted for it in either dialect. C block scope is out of scope for the
24684    /// dialect change, and this test proves the change did not disturb it.
24685    #[test]
24686    #[hardwon(
24687        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
24688    )]
24689    fn function_local_tags_are_unchanged_in_both_dialects() {
24690        let source =
24691            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
24692        for name in ["y.c", "y.cpp"] {
24693            let parsed = parse_cpp_declarations(source, name);
24694            let declarations = parsed.declarations();
24695            assert!(
24696                declarations
24697                    .iter()
24698                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
24699                "{name}: {declarations:?}"
24700            );
24701            for tag in ["localtag", "deeper", "localtag$deeper"] {
24702                assert!(
24703                    declarations.iter().all(|unit| unit.fq_name() != tag),
24704                    "{name} must not mint {tag}, got {declarations:?}"
24705                );
24706            }
24707        }
24708    }
24709
24710    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
24711    /// re-scope: the typedef name is identical in both dialects.
24712    #[test]
24713    #[hardwon(
24714        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
24715    )]
24716    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
24717        let source = "typedef struct { int v; } T;\n";
24718        for name in ["t.c", "t.cpp"] {
24719            let parsed = parse_cpp_declarations(source, name);
24720            let declarations = parsed.declarations();
24721            assert!(
24722                declarations
24723                    .iter()
24724                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
24725                "{name}: {declarations:?}"
24726            );
24727        }
24728    }
24729
24730    /// The same principle for every anonymous aggregate the walk indexes, not
24731    /// just the typedef one: a promoted anonymous union in a member list, an
24732    /// anonymous struct given a receiver type by its declarator, a
24733    /// function-local aggregate, and one inside an inline member function
24734    /// body. The C++ reading used to mint none of these, so a pure C++
24735    /// workspace indexed no members at all (#3301).
24736    #[test]
24737    #[hardwon(
24738        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
24739    )]
24740    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
24741        let sources = [
24742            "struct outer { union { int a; float b; }; };\n",
24743            "struct outer { struct { int *ops; } sock; };\n",
24744            "void run(void) { struct { int v; } item; }\n",
24745            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
24746        ];
24747        for source in sources {
24748            let names = |name: &str| {
24749                parse_cpp_declarations(source, name)
24750                    .declarations()
24751                    .iter()
24752                    .map(|unit| unit.fq_name())
24753                    .collect::<std::collections::BTreeSet<_>>()
24754            };
24755            let c_names = names("a.c");
24756            let cpp_names = names("a.cpp");
24757            assert_eq!(
24758                c_names, cpp_names,
24759                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
24760            );
24761            assert!(
24762                c_names
24763                    .iter()
24764                    .any(|name| name.contains('.') && name != "outer"),
24765                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
24766            );
24767        }
24768    }
24769
24770    #[test]
24771    #[hardwon(
24772        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
24773    )]
24774    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
24775        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
24776        let parsed = parse_cpp_declarations(source, "socket.c");
24777        let declarations = parsed.declarations();
24778        assert_eq!(
24779            declarations
24780                .iter()
24781                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
24782                .count(),
24783            1,
24784            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
24785        );
24786        for expected in [
24787            "PAL_HANDLE",
24788            "PAL_HANDLE.sock",
24789            "PAL_HANDLE$sock",
24790            "PAL_HANDLE$sock.ops",
24791        ] {
24792            assert!(
24793                declarations.iter().any(|unit| unit.fq_name() == expected),
24794                "expected {expected}, got {declarations:?}"
24795            );
24796        }
24797    }
24798
24799    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
24800    /// every (node, class-like name) pair a tree offers, and require the same
24801    /// answer from both.
24802    ///
24803    /// The release build has no `debug_assertions` agreement check, so this is
24804    /// what pins the two together there.  Both query orders are exercised:
24805    /// document order is what the walk does, and reverse order proves that a
24806    /// question about an earlier byte than one already answered is still
24807    /// filtered back to its own prefix rather than answered from the wider
24808    /// fold.
24809    ///
24810    /// Returns how many questions were answered with a namespace, so a fixture
24811    /// can assert it actually reached the path (#2754).
24812    fn namespace_forward_scan_agreement(source: &str) -> usize {
24813        let mut parser = tree_sitter::Parser::new();
24814        parser
24815            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24816            .unwrap();
24817        let tree = parser.parse(source, None).unwrap();
24818        let root = tree.root_node();
24819        let ancestry = ParentIndex::new(root);
24820
24821        let mut nodes = Vec::new();
24822        let mut names = std::collections::BTreeSet::new();
24823        let mut cursor = root.walk();
24824        let mut stack = vec![root];
24825        while let Some(node) = stack.pop() {
24826            if matches!(
24827                node.kind(),
24828                "class_specifier" | "struct_specifier" | "union_specifier"
24829            ) && let Some(name) = class_like_name(node, source, &ancestry)
24830            {
24831                names.insert(name);
24832            }
24833            nodes.push(node);
24834            stack.extend(node.named_children(&mut cursor));
24835        }
24836        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
24837        assert!(!names.is_empty(), "fixture declares no class-like name");
24838
24839        let mut answered = 0usize;
24840        for reversed in [false, true] {
24841            let mut scan = CppNamespaceForwardScan::default();
24842            let ordered: Vec<_> = if reversed {
24843                nodes.iter().rev().copied().collect()
24844            } else {
24845                nodes.clone()
24846            };
24847            answered = 0;
24848            for node in ordered {
24849                for name in &names {
24850                    scan.advance_to(root, node.start_byte(), source, &ancestry);
24851                    let carried = scan.unique_earlier_forward(name, node);
24852                    assert_eq!(
24853                        carried,
24854                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
24855                        "carried-forward scan and prefix scan disagree about {name} at \
24856                         {} node starting at byte {} (reversed order: {reversed})",
24857                        node.kind(),
24858                        node.start_byte()
24859                    );
24860                    answered += usize::from(carried.is_some());
24861                }
24862            }
24863        }
24864        answered
24865    }
24866
24867    /// A malformed namespace whose forward declarations are the only identity
24868    /// signal left for the class definitions tree-sitter pushed out to file
24869    /// scope.  Both recovered classes open the guard; only the first one is
24870    /// separated from the namespace by nothing but recovery trivia, so only the
24871    /// first one borrows.  The carried-forward scan has to reproduce both
24872    /// answers.
24873    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
24874namespace ns {
24875class Widget;
24876class Gadget;
24877int x = ;
24878}
24879class API Widget {
24880public:
24881    void first();
24882};
24883class API Gadget {
24884public:
24885    void second();
24886};
24887"#;
24888
24889    #[test]
24890    #[hardwon(
24891        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
24892    )]
24893    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
24894        assert!(
24895            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
24896            "the fixture must actually reach the namespace-borrow path"
24897        );
24898
24899        // Nothing here may be answered, and the two paths have to agree about
24900        // that too: a clean namespace is not an identity proof, two forwards of
24901        // one name are ambiguous rather than a guess, and a forward inside a
24902        // function body is not at namespace scope.
24903        for source in [
24904            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
24905            r#"#define API
24906namespace ns {
24907class Widget;
24908class Widget;
24909int x = ;
24910}
24911class API Widget {
24912public:
24913    void method();
24914};
24915"#,
24916            r#"#define API
24917namespace ns {
24918void host() {
24919    class Widget;
24920}
24921int x = ;
24922}
24923class API Widget {
24924public:
24925    void method();
24926};
24927"#,
24928        ] {
24929            assert_eq!(
24930                namespace_forward_scan_agreement(source),
24931                0,
24932                "no borrow is justified here: {source}"
24933            );
24934        }
24935    }
24936
24937    /// Drive the region reparse and the whitespace-padded reparse it replaced
24938    /// over the same region and require identical trees.
24939    ///
24940    /// The release build has no `debug_assertions` agreement check, so this is
24941    /// what pins the two together there. Each body is reparsed at its own
24942    /// offset and again after a long prefix, because the prefix is the whole
24943    /// difference between the two techniques: the padded parse lexes it as
24944    /// whitespace, the included-range parse never sees it, and the tree has to
24945    /// come out the same either way (#2788).
24946    fn fragmented_class_reparse_agreement(body: &str) {
24947        for prefix in [
24948            String::new(),
24949            "// leading comment\n".to_string(),
24950            // A body starts just after its class head's `{`, which is normally
24951            // mid-line: the padded parse then has spaces before the region on
24952            // the region's own line, and the included-range parse has nothing
24953            // at all before it.
24954            "class Widget : public Base { ".to_string(),
24955            "namespace filler {\n".to_string()
24956                + &"struct Filler { int member; };\n".repeat(200)
24957                + "}\n",
24958            "namespace filler {\n".to_string()
24959                + &"struct Filler { int member; };\n".repeat(200)
24960                + "}\nclass Widget : public Base { ",
24961        ] {
24962            let source = format!("{prefix}{body}");
24963            let start = prefix.len();
24964            let end = source.len();
24965            let region = cpp_reparse_fragmented_class_body(&source, start, end)
24966                .expect("the region reparse must produce a tree");
24967            let padded = cpp_reparse_padded_class_body(&source, start, end)
24968                .expect("the padded reparse must produce a tree");
24969            assert_eq!(
24970                cpp_tree_shape(&region),
24971                cpp_tree_shape(&padded),
24972                "region and padded reparse disagree at offset {start} of {end} bytes"
24973            );
24974            assert_eq!(
24975                region.root_node().start_byte(),
24976                start,
24977                "the reparsed region keeps its original offsets"
24978            );
24979        }
24980    }
24981
24982    #[test]
24983    #[hardwon(
24984        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
24985    )]
24986    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
24987        // A conditional immediately after an access label: the shape the padded
24988        // technique was kept for, because the directive and its macro name
24989        // become an ERROR plus the following declaration's apparent type.
24990        fragmented_class_reparse_agreement(
24991            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
24992        );
24993        fragmented_class_reparse_agreement(
24994            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
24995        );
24996        // The merged inline constructor and the nested fragmented bodies the
24997        // #938 recovery reads out of a reparse.
24998        fragmented_class_reparse_agreement(
24999            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
25000        );
25001        fragmented_class_reparse_agreement(
25002            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
25003        );
25004        // A body that is not member-shaped at all still has to reparse the same
25005        // way, because the admission gate reads the tree to reject it.
25006        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
25007    }
25008
25009    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
25010        let mut siblings = Vec::new();
25011        let mut anchor = node;
25012        while let Some(parent) = anchor.parent() {
25013            let at_translation_unit = parent.kind() == "translation_unit";
25014            let mut sibling = anchor.next_named_sibling();
25015            while let Some(current) = sibling {
25016                if at_translation_unit
25017                    && (current.kind() == "namespace_definition"
25018                        || (current.kind() == "function_definition"
25019                            && first_class_like_child(current).is_some()))
25020                {
25021                    return siblings;
25022                }
25023                siblings.push(current);
25024                if cpp_is_stray_close_brace(current, source) {
25025                    if let Some(semicolon) = current
25026                        .next_named_sibling()
25027                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
25028                    {
25029                        siblings.push(semicolon);
25030                    }
25031                    return siblings;
25032                }
25033                if current.start_byte() >= node.end_byte()
25034                    && matches!(current.kind(), "ERROR" | "labeled_statement")
25035                    && cpp_nested_stray_close_brace(current, source).is_some()
25036                {
25037                    return siblings;
25038                }
25039                sibling = current.next_named_sibling();
25040            }
25041            anchor = parent;
25042        }
25043        siblings
25044    }
25045
25046    #[test]
25047    #[hardwon(
25048        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
25049    )]
25050    fn following_named_siblings_cursor_matches_native_traversal() {
25051        // The damaged #2924 shape scatters members across container siblings,
25052        // and the anonymous punctuation forces the cursor to filter children
25053        // exactly as Node::next_named_sibling does.
25054        let source = r#"
25055namespace api {
25056class PROJECT_API(2, 0) Widget : public virtual Base {
25057  public:
25058    Widget() {}
25059    void first();
25060};
25061enum After { Value };
25062}
25063"#;
25064        let mut parser = tree_sitter::Parser::new();
25065        parser
25066            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25067            .unwrap();
25068        let tree = parser.parse(source, None).unwrap();
25069        let root = tree.root_node();
25070        assert!(root.has_error(), "fixture must remain damaged");
25071
25072        let ancestry = ParentIndex::new(root);
25073        let mut stack = vec![root];
25074        let mut checked = 0;
25075        let mut damaged = 0;
25076        let mut anonymous = 0;
25077        let mut nonempty_sequences = 0;
25078        let mut named_then_anonymous = 0;
25079        while let Some(node) = stack.pop() {
25080            let expected = native_following_named_siblings(node, source)
25081                .iter()
25082                .map(Node::id)
25083                .collect::<Vec<_>>();
25084            let actual = cpp_following_named_siblings(node, source, &ancestry)
25085                .iter()
25086                .map(Node::id)
25087                .collect::<Vec<_>>();
25088            assert_eq!(
25089                actual,
25090                expected,
25091                "cursor traversal diverged at byte {}",
25092                node.start_byte()
25093            );
25094
25095            let mut cursor = node.walk();
25096            let children = node.children(&mut cursor).collect::<Vec<_>>();
25097            stack.extend(children.iter().copied());
25098            checked += 1;
25099            damaged += usize::from(node.is_error() || node.is_missing());
25100            anonymous += usize::from(!node.is_named());
25101            nonempty_sequences += usize::from(!actual.is_empty());
25102            named_then_anonymous += usize::from(
25103                children
25104                    .windows(2)
25105                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
25106            );
25107        }
25108        assert!(checked > 0, "fixture must provide nodes to compare");
25109        assert!(damaged > 0, "fixture must cover damaged nodes");
25110        assert!(anonymous > 0, "fixture must cover anonymous nodes");
25111        assert!(
25112            nonempty_sequences > 0,
25113            "fixture must exercise following named siblings"
25114        );
25115        assert!(
25116            named_then_anonymous > 0,
25117            "fixture must exercise anonymous nodes following a named anchor"
25118        );
25119    }
25120
25121    /// A header shaped like the generated ones this walk is slow on: many
25122    /// enums, classes whose members share the enums' names, nested enums, an
25123    /// ownerless enumerator, and namespaced repeats of all of it.
25124    fn many_enums_and_mixed_declarations() -> String {
25125        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
25126        for index in 0..40 {
25127            let _ = write!(
25128                source,
25129                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
25130                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
25131                 class Color{index}Like {{ public: int member{index}; }};\n"
25132            );
25133        }
25134        source.push_str("namespace outer {\n");
25135        for index in 0..20 {
25136            let _ = write!(
25137                source,
25138                "enum Shade{index} {{ DARK{index} }};\n\
25139                 struct Shade{index}Holder {{ int field{index}; }};\n"
25140            );
25141        }
25142        source.push_str("}\n");
25143        source
25144    }
25145
25146    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
25147    /// every question a fixture's declarations can ask, and require the same
25148    /// answer from both.
25149    ///
25150    /// The release build has no `debug_assertions` agreement check, so this is
25151    /// what pins the two together there. The index is fed one declaration at a
25152    /// time and every question is re-asked after each one, which is what proves
25153    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
25154    /// test. Each of the fixture's own fields is also fed in restated as a
25155    /// declaration of another file: the scan ignores those because it asks
25156    /// about the asking unit's own source, and the index has to ignore them for
25157    /// the same reason.
25158    ///
25159    /// Returns how many questions the fixture answered `true`, so a caller can
25160    /// assert that it actually reached the path (#2786).
25161    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
25162        let parsed = parse_cpp_declarations(source, name);
25163        let file = ProjectFile::new(std::env::temp_dir(), name);
25164        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
25165
25166        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
25167        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
25168
25169        let foreign: Vec<CodeUnit> = declarations
25170            .iter()
25171            .filter(|unit| unit.kind() == CodeUnitType::Field)
25172            .map(|unit| {
25173                CodeUnit::new_fq(
25174                    elsewhere.clone(),
25175                    unit.kind(),
25176                    unit.package_name().to_string(),
25177                    unit.short_name().to_string(),
25178                    unit.fq().clone(),
25179                )
25180            })
25181            .collect();
25182
25183        // One owner chain deeper than any C++ short name reaches today
25184        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
25185        // the member). The scan asks `starts_with("owner.")`, so a field like
25186        // this answers for every owner in its chain, and the index has to
25187        // record every one of them rather than only the innermost.
25188        let mut packages: Vec<String> = declarations
25189            .iter()
25190            .map(|unit| unit.package_name().to_string())
25191            .collect();
25192        packages.push(String::new());
25193        packages.sort();
25194        packages.dedup();
25195        let deeper: Vec<CodeUnit> = packages
25196            .iter()
25197            .map(|package_name| {
25198                CodeUnit::new_fq(
25199                    file.clone(),
25200                    CodeUnitType::Field,
25201                    package_name.clone(),
25202                    "SynthOwner.middle.leaf".to_string(),
25203                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
25204                )
25205            })
25206            .collect();
25207
25208        // Every (package, owner) pair anything could ask about: each unit's own
25209        // short name, each dotted prefix of it, and the empty owner an
25210        // anonymous enum asks with (#2140).
25211        let mut questions: Vec<(String, String)> = Vec::new();
25212        for unit in declarations.iter().chain(deeper.iter()) {
25213            let package_name = unit.package_name().to_string();
25214            let short_name = unit.short_name();
25215            questions.push((package_name.clone(), short_name.to_string()));
25216            questions.push((package_name.clone(), String::new()));
25217            for (offset, _) in short_name.match_indices('.') {
25218                questions.push((package_name.clone(), short_name[..offset].to_string()));
25219            }
25220        }
25221        questions.sort();
25222        questions.dedup();
25223
25224        let mut index = CppFieldOwnerIndex::default();
25225        let mut recorded: Vec<&CodeUnit> = Vec::new();
25226        let mut answered = 0usize;
25227        for unit in foreign
25228            .iter()
25229            .chain(declarations.iter())
25230            .chain(deeper.iter())
25231        {
25232            index.record(unit, &file);
25233            recorded.push(unit);
25234            for (package_name, owner_short_name) in &questions {
25235                let carried = index.owns_fields(package_name, owner_short_name);
25236                assert_eq!(
25237                    carried,
25238                    cpp_declarations_hold_owned_fields(
25239                        recorded.iter().copied(),
25240                        &file,
25241                        package_name,
25242                        owner_short_name
25243                    ),
25244                    "the carried field index and the declaration scan disagree about \
25245                     {package_name:?}/{owner_short_name:?} after recording {}",
25246                    unit.fq_name()
25247                );
25248                answered += usize::from(carried);
25249            }
25250        }
25251
25252        // The whole-set build the first question performs must land on the same
25253        // index the incremental record built.
25254        let rebuilt = CppFieldOwnerIndex::of(
25255            foreign
25256                .iter()
25257                .chain(declarations.iter())
25258                .chain(deeper.iter()),
25259            &file,
25260        );
25261        for (package_name, owner_short_name) in &questions {
25262            assert_eq!(
25263                rebuilt.owns_fields(package_name, owner_short_name),
25264                index.owns_fields(package_name, owner_short_name),
25265                "a rebuilt index must answer what the incremental one answers for \
25266                 {package_name:?}/{owner_short_name:?}"
25267            );
25268        }
25269        answered
25270    }
25271
25272    /// The one thing the field index cannot absorb by addition: a deferred
25273    /// replacement of a declaration that owns children removes those children.
25274    ///
25275    /// The first enum builds the index, the struct records `Color.RED` into it,
25276    /// the body-less second `Color` replaces the first and takes `Color.RED`
25277    /// with it, and the last enum then asks about owner `Color`. An index that
25278    /// survived that removal answers `true` where the declarations say `false`,
25279    /// which is exactly what the in-walk agreement assertion catches (#2786).
25280    #[test]
25281    #[hardwon(
25282        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
25283    )]
25284    fn a_replacement_that_removes_children_drops_the_field_index() {
25285        let source =
25286            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
25287        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
25288        let mut names: Vec<_> = parsed
25289            .declarations()
25290            .iter()
25291            .map(|unit| unit.fq_name())
25292            .collect();
25293        names.sort();
25294        assert_eq!(
25295            names,
25296            vec![
25297                "Color".to_string(),
25298                "First".to_string(),
25299                "First.A".to_string()
25300            ],
25301            "the replaced Color owns no field any more"
25302        );
25303    }
25304
25305    /// A recovery that re-declares what the file already declared mints
25306    /// nothing.
25307    ///
25308    /// The reparse walk replaces the outer `Widget`, which removes its method,
25309    /// and then re-creates that method from the region. Creation alone would
25310    /// call the method recovered; it was there before the recovery opened, so
25311    /// the recovered set is empty and only the region's own reparse window is
25312    /// recorded (#2787).
25313    #[test]
25314    #[hardwon(
25315        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
25316    )]
25317    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
25318        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
25319                      BEGIN_NS\n\
25320                      namespace demo { struct Widget { void doWork(); }; }\n\
25321                      END_NS\n";
25322        let parsed = parse_cpp_declarations(source, "restored.cpp");
25323        let recovered: Vec<String> = parsed
25324            .materialization_records
25325            .iter()
25326            .filter_map(|record| match record {
25327                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
25328                _ => None,
25329            })
25330            .collect();
25331        assert!(
25332            recovered.is_empty(),
25333            "the region declares nothing the file did not already declare: {recovered:?}"
25334        );
25335        let mut names: Vec<String> = parsed
25336            .declarations()
25337            .iter()
25338            .map(|unit| unit.fq_name())
25339            .collect();
25340        names.sort();
25341        assert_eq!(
25342            names,
25343            vec![
25344                "demo".to_string(),
25345                "demo.Widget".to_string(),
25346                "demo.Widget.doWork".to_string(),
25347            ]
25348        );
25349    }
25350
25351    /// Four macro-sentinel recoveries in one file (#941). Each one must record
25352    /// exactly the declarations it minted -- not the ones an earlier recovery
25353    /// minted, and not the file's other declarations -- in start-byte order,
25354    /// and each record must carry its own reparse window (#2787).
25355    #[test]
25356    #[hardwon(
25357        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
25358    )]
25359    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
25360        let mut source = String::new();
25361        for index in 0..4 {
25362            let _ = write!(
25363                source,
25364                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
25365            );
25366        }
25367        source.push_str("void outside() {}\n");
25368        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
25369
25370        let recovered: Vec<(String, (usize, usize))> = parsed
25371            .materialization_records
25372            .iter()
25373            .filter_map(|record| match record {
25374                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
25375                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
25376                }
25377                _ => None,
25378            })
25379            .collect();
25380
25381        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
25382        for index in 0..4 {
25383            // The window the reparse covers: everything after the opening
25384            // sentinel token up to the newline before the closing one.
25385            let region = format!("namespace demo{index}");
25386            let region_start = source.find(&region).expect("each region is in the source");
25387            let start = source[..region_start]
25388                .rfind("BEGIN_NS")
25389                .expect("each region opens with a sentinel")
25390                + "BEGIN_NS".len();
25391            let end = start
25392                + source[start..]
25393                    .find("END_NS")
25394                    .expect("each region closes with a sentinel")
25395                - 1;
25396            let window = (start, end);
25397            for name in [
25398                format!("demo{index}"),
25399                format!("demo{index}.Widget{index}"),
25400                format!("demo{index}.Widget{index}.doWork{index}"),
25401            ] {
25402                expected.push((name, window));
25403            }
25404        }
25405        assert_eq!(
25406            recovered, expected,
25407            "each recovery records its own minted declarations, in order"
25408        );
25409        assert!(
25410            parsed
25411                .declarations()
25412                .iter()
25413                .any(|unit| unit.fq_name() == "outside"),
25414            "the declaration outside every region stays parsed and unrecovered"
25415        );
25416    }
25417
25418    #[test]
25419    #[hardwon(
25420        "69c2e1f7e41c: C++ declaration recovery regressed for carried forward field index answers what the declaration scan answers"
25421    )]
25422    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
25423        assert!(
25424            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
25425            "the fixture must actually own fields"
25426        );
25427
25428        // The shapes that make the two implementations diverge if the index
25429        // records the wrong keys: a nested enum's owner is its whole dotted
25430        // chain, a class named like an enum owns fields under that same name, a
25431        // `$` in a short name is an owner separator the dotted prefix rule must
25432        // not split on, and the same enum name in two namespaces is two owners.
25433        for (source, name) in [
25434            ("struct S { enum E { V }; };\n", "nested.hpp"),
25435            (
25436                "enum Color { RED };\nstruct Color { int RED; };\n",
25437                "class-like.c",
25438            ),
25439            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
25440            (
25441                "enum E { V };\nnamespace ns { enum E { V }; }\n",
25442                "repeated.hpp",
25443            ),
25444            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
25445        ] {
25446            field_owner_index_agreement(source, name);
25447        }
25448    }
25449
25450    /// `blocks` copies of one plain class-with-methods template. The class the
25451    /// question is about is always the first one, so the only thing that
25452    /// changes between two of these sources is how much unrelated tree
25453    /// surrounds it.
25454    fn repeated_class_blocks(blocks: usize) -> String {
25455        let mut source = String::from("namespace demo {\n");
25456        for index in 0..blocks {
25457            let _ = write!(
25458                source,
25459                "\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"
25460            );
25461        }
25462        source.push_str("\n}\n");
25463        source
25464    }
25465
25466    /// #1496: asking whether a recovered class shape claims one range must cost
25467    /// the path to that range, not a pass over the whole translation unit.
25468    ///
25469    /// The C++ inverse scan asks this once per candidate type reference, so a
25470    /// whole-tree walk makes one file's scan quadratic in its own size. Both
25471    /// sources here answer `None` -- these are plain classes that no recovery
25472    /// shape claims -- which is exactly the case that used to pay full price.
25473    #[test]
25474    #[hardwon(
25475        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
25476    )]
25477    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
25478        let mut answers = Vec::new();
25479        let mut visits = Vec::new();
25480        let mut node_counts = Vec::new();
25481        for blocks in [200usize, 400] {
25482            let source = repeated_class_blocks(blocks);
25483            let mut parser = tree_sitter::Parser::new();
25484            parser
25485                .set_language(&tree_sitter_cpp::LANGUAGE.into())
25486                .unwrap();
25487            let tree = parser.parse(&source, None).unwrap();
25488            let start_byte = source.find("class Widget0 ").expect("first class");
25489            let end_byte = start_byte
25490                + source[start_byte..]
25491                    .find("};")
25492                    .expect("first class terminator")
25493                + "};".len();
25494            let range = Range {
25495                start_byte,
25496                end_byte,
25497                start_line: 0,
25498                end_line: 0,
25499            };
25500            reset_recovered_class_body_node_visits_for_test();
25501            let recovered_export_classes =
25502                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
25503            answers.push(recovered_class_body_at(
25504                &recovered_export_classes,
25505                tree.root_node(),
25506                &source,
25507                "Widget0",
25508                &range,
25509            ));
25510            visits.push(recovered_class_body_node_visits_for_test());
25511            let mut nodes = 0usize;
25512            let mut stack = vec![tree.root_node()];
25513            while let Some(node) = stack.pop() {
25514                nodes += 1;
25515                let mut cursor = node.walk();
25516                stack.extend(node.named_children(&mut cursor));
25517            }
25518            node_counts.push(nodes);
25519        }
25520
25521        assert_eq!(
25522            answers,
25523            vec![None, None],
25524            "no recovered shape claims a plain class"
25525        );
25526        assert_eq!(
25527            visits[0], visits[1],
25528            "the walk must follow the range's own path, so doubling the unrelated \
25529             classes must not change the node count: {visits:?} over trees of \
25530             {node_counts:?} nodes"
25531        );
25532        assert!(
25533            visits[1] * 20 < node_counts[1],
25534            "the walk must stay far below one pass over the tree: {visits:?} over \
25535             trees of {node_counts:?} nodes"
25536        );
25537    }
25538
25539    #[test]
25540    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
25541    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
25542        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
25543        let mut parser = tree_sitter::Parser::new();
25544        parser
25545            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25546            .expect("C++ grammar");
25547        let tree = parser.parse(source, None).expect("fixture tree");
25548        let mut stack = vec![tree.root_node()];
25549        let mut recovered = None;
25550        while let Some(node) = stack.pop() {
25551            if node.kind() == "field_declaration"
25552                && let Some(field) = recovered_function_like_field_declarator(node, source)
25553            {
25554                recovered = Some((node, field.name));
25555                break;
25556            }
25557            let mut cursor = node.walk();
25558            stack.extend(node.named_children(&mut cursor));
25559        }
25560        let (declaration, name) = recovered.unwrap_or_else(|| {
25561            panic!(
25562                "pointer-wrapped macro field was not recovered: {}",
25563                tree.root_node().to_sexp()
25564            )
25565        });
25566        assert_eq!(node_text(name, source), "handshake");
25567        let recovered =
25568            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
25569        assert_eq!(recovered.pointer_depth(), 1);
25570        assert_eq!(
25571            render_cpp_field_signature(declaration, name, source),
25572            "struct handshake * handshake;"
25573        );
25574    }
25575
25576    #[test]
25577    #[hardwon(
25578        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25579    )]
25580    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
25581        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
25582        let mut parser = tree_sitter::Parser::new();
25583        parser
25584            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25585            .expect("C++ grammar");
25586        let tree = parser.parse(source, None).expect("fixture tree");
25587        let mut stack = vec![tree.root_node()];
25588        let mut recovered = None;
25589        while let Some(node) = stack.pop() {
25590            if let Some(field) = recovered_pyobject_head_field(node, source) {
25591                recovered = Some((node, field));
25592                break;
25593            }
25594            let mut cursor = node.walk();
25595            stack.extend(node.named_children(&mut cursor));
25596        }
25597        let (declaration, recovered) = recovered.unwrap_or_else(|| {
25598            panic!(
25599                "pointer field after PyObject_HEAD was not recovered: {}",
25600                tree.root_node().to_sexp()
25601            )
25602        });
25603        assert_eq!(node_text(recovered.name, source), "image");
25604        assert_eq!(recovered.pointer_depth(), 1);
25605        assert_eq!(
25606            render_cpp_field_signature(declaration, recovered.declarator, source),
25607            "ImagingObject *image;"
25608        );
25609        assert_eq!(
25610            crate::source_facts::capture_field_type(declaration, "image", source),
25611            (
25612                Some(
25613                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
25614                        type_text: "ImagingObject".to_owned(),
25615                        indirection: 1,
25616                        binds_indirectly: true,
25617                        template_arguments: None,
25618                    }
25619                ),
25620                false,
25621            ),
25622            "canonical field facts must use the recovered type, not the macro token"
25623        );
25624    }
25625
25626    #[test]
25627    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
25628        let source = "class outer { class inner { int v; }; };\n";
25629        let c_parsed = parse_cpp_declarations(source, "k.c");
25630        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
25631        let c_declarations = c_parsed.declarations();
25632        let cpp_declarations = cpp_parsed.declarations();
25633        assert!(
25634            c_declarations
25635                .iter()
25636                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
25637            "{c_declarations:?}"
25638        );
25639        assert_eq!(
25640            c_declarations
25641                .iter()
25642                .map(|unit| unit.fq_name())
25643                .collect::<std::collections::BTreeSet<_>>(),
25644            cpp_declarations
25645                .iter()
25646                .map(|unit| unit.fq_name())
25647                .collect::<std::collections::BTreeSet<_>>()
25648        );
25649    }
25650    #[test]
25651    fn callable_metadata_records_c_receiver_contracts_structurally() {
25652        const SOURCE: &str = concat!(
25653            "static long accumulate(const long *lines, int count) { return count; }\n",
25654            "\n",
25655            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
25656        );
25657        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
25658
25659        assert_eq!(
25660            recorded_callable_modifiers(&parsed),
25661            vec![
25662                "accumulate decl_only=false static=false constructor=false \
25663                 visibility=Unknown"
25664                    .to_string(),
25665                "total decl_only=false static=false constructor=false visibility=Unknown"
25666                    .to_string(),
25667            ],
25668        );
25669    }
25670    #[test]
25671    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
25672        const SOURCE: &str = concat!(
25673            "namespace shop {\n",
25674            "\n",
25675            "class Client {\n",
25676            "public:\n",
25677            "    Client();\n",
25678            "    void send(int order);\n",
25679            "    static int count();\n",
25680            "protected:\n",
25681            "    virtual int audit(int n) const { return n; }\n",
25682            "private:\n",
25683            "    int tally(int n) { return n; }\n",
25684            "};\n",
25685            "\n",
25686            "struct Ledger {\n",
25687            "    void record(int n) { (void)n; }\n",
25688            "};\n",
25689            "\n",
25690            "Client::Client() {}\n",
25691            "void Client::send(int order) { (void)order; }\n",
25692            "int Client::count() { return 0; }\n",
25693            "\n",
25694            "int total(int order) { return order; }\n",
25695            "\n",
25696            "}\n",
25697        );
25698        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
25699
25700        assert_eq!(
25701            recorded_callable_modifiers(&parsed),
25702            vec![
25703                "shop.Client.Client decl_only=false static=false constructor=true \
25704                 visibility=Unknown"
25705                    .to_string(),
25706                "shop.Client.Client decl_only=true static=false constructor=true \
25707                 visibility=Public"
25708                    .to_string(),
25709                "shop.Client.audit decl_only=false static=false constructor=false \
25710                 visibility=Protected"
25711                    .to_string(),
25712                "shop.Client.count decl_only=false static=false constructor=false \
25713                 visibility=Unknown"
25714                    .to_string(),
25715                "shop.Client.count decl_only=true static=true constructor=false \
25716                 visibility=Public"
25717                    .to_string(),
25718                "shop.Client.send decl_only=false static=false constructor=false \
25719                 visibility=Unknown"
25720                    .to_string(),
25721                "shop.Client.send decl_only=true static=false constructor=false \
25722                 visibility=Public"
25723                    .to_string(),
25724                "shop.Client.tally decl_only=false static=false constructor=false \
25725                 visibility=Private"
25726                    .to_string(),
25727                "shop.Ledger.record decl_only=false static=false constructor=false \
25728                 visibility=Public"
25729                    .to_string(),
25730                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
25731                    .to_string(),
25732            ],
25733        );
25734    }
25735}