Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
11use brokk_bifrost_core::analyzer::common::{
12    parse_source_ranges_with_cancellation, parse_source_region,
13};
14pub use brokk_bifrost_core::analyzer::cpp_facts::{
15    CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
16    CppComparableSlot,
17};
18use brokk_bifrost_core::analyzer::fq_name::{
19    FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
20};
21use brokk_bifrost_core::analyzer::model::LanguageDialect;
22use brokk_bifrost_core::analyzer::model::{
23    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
24    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
25    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
26    ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
27    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
28};
29use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
30use brokk_bifrost_core::analyzer::source_facts::{
31    PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
32    SourceOccurrenceProvenance,
33};
34use brokk_bifrost_core::analyzer::statement_facts::CppStatementFactRows;
35use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
36use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
37use brokk_bifrost_core::analyzer::structural::materialization::{
38    GenerationKind, MaterializationRecord,
39};
40use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
41use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
42use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
43use brokk_bifrost_core::analyzer::tree_walk::{
44    NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
45    named_children_iter, push_children_reversed, push_named_children_reversed, subtree_contains,
46    walk_named_tree_preorder,
47};
48use brokk_bifrost_core::analyzer::usages::common::same_node;
49use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
50use brokk_bifrost_core::hash::{HashMap, HashSet};
51use regex::Regex;
52use std::cell::RefCell;
53use 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/// [`cpp_namespace_name_components`] with each level's own name token range,
155/// for the one-Module-per-level declarations of the `namespace a::b`
156/// shorthand: the name a level records must come from the token that spells
157/// it (#4292), not from whichever construct the walk reached first.
158fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
159    let mut components = Vec::new();
160    let mut stack = vec![node];
161    while let Some(current) = stack.pop() {
162        match current.kind() {
163            "namespace_identifier" | "identifier" => {
164                components.push((
165                    normalize_cpp_whitespace(node_text(current, source)),
166                    cpp_declaration_range(current),
167                ));
168            }
169            "nested_namespace_specifier" => {
170                for index in (0..current.named_child_count()).rev() {
171                    stack.push(
172                        current
173                            .named_child(index)
174                            .expect("index below the node's own named child count"),
175                    );
176                }
177            }
178            _ => return Vec::new(),
179        }
180    }
181    if components.iter().any(|(name, _)| name.is_empty()) {
182        return Vec::new();
183    }
184    components
185}
186
187/// The historical reading of a namespace name node: its whole source text as
188/// one component, with a leading global `::` marker dropped so the caller's
189/// global-scope handling stays the AST boundary rather than a text prefix.
190///
191/// This is the recovery path for source outside the C++ grammar, so the text it
192/// returns can carry a separator that names nothing. react-native-windows
193/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
194/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
195/// trailing separator with no tail. The caller stores this component in
196/// `short_name` and derives the fq by splitting it back apart, so an empty tail
197/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
198/// here, at the one place the malformed text enters, rather than leaving each
199/// consumer to guard (#2353, a variant of #1878).
200fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
201    let start = node
202        .child(0)
203        .filter(|child| !child.is_named() && child.kind() == "::")
204        .map_or(node.start_byte(), |marker| marker.end_byte());
205    let text = normalize_cpp_whitespace(
206        source
207            .get(start..node.end_byte())
208            .expect("namespace name node covers one source range"),
209    );
210    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
211    if text.is_empty() {
212        return Vec::new();
213    }
214    vec![text]
215}
216
217/// The namespace definition ancestors of `node`, ordered from outermost to innermost.
218pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
219    let mut namespaces = Vec::new();
220    let mut current = node.parent();
221    while let Some(parent) = current {
222        if parent.kind() == "namespace_definition" {
223            namespaces.push(parent);
224        }
225        current = parent.parent();
226    }
227    namespaces.reverse();
228    namespaces
229}
230
231/// Return the named namespace path that structurally encloses `node`.
232///
233/// This intentionally follows namespace AST ancestors rather than inspecting
234/// source text. Anonymous namespaces are not representable in the legacy C++
235/// package field, so a path containing one fails closed.
236fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
237    let mut components = Vec::new();
238    for current in enclosing_cpp_namespace_nodes(node) {
239        let name_node = current.child_by_field_name("name")?;
240        let name = normalize_cpp_whitespace(node_text(name_node, source));
241        if name.is_empty() {
242            return None;
243        }
244        components.push(name);
245    }
246    if components.is_empty() {
247        return None;
248    }
249    // Same shared empty-component decision as `cpp_push_package`, which splits
250    // this string back apart: an enclosing namespace whose own name node is
251    // malformed contributes a component carrying its own separator (#2353).
252    Some(
253        normalize_joined(
254            &components.join(CPP_PACKAGE_SEPARATOR),
255            CPP_PACKAGE_SEPARATOR,
256        )
257        .into_owned(),
258    )
259}
260
261/// Nested-class `$` join for short names. An anonymous parent class (empty
262/// short_name) contributes no segment: the FqName bridge drops empty
263/// components, so a bare `parent$child` join would desync `short_name` from
264/// the fq and trip the package/short boundary assert in
265/// `CodeUnit::with_signature_and_fq` (#2140).
266fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
267    cpp_join_short_name(parent_short, name, '$')
268}
269
270/// Member `.` join for short names; same anonymous-parent guard as
271/// [`cpp_join_nested_short`] (#2140).
272fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
273    cpp_join_short_name(parent_short, name, '.')
274}
275
276fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
277    if parent_short.is_empty() {
278        name.to_string()
279    } else {
280        format!("{parent_short}{separator}{name}")
281    }
282}
283
284/// Structural fq for a leaf declaration: the parent unit's fq plus this
285/// declaration's own name as one segment (or the package segments plus the
286/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
287/// chain, so a literal `$` inside a source identifier (Cython template
288/// substitution points, gcc `$`-identifiers) survives instead of corrupting
289/// the chain and tripping the package/short boundary assert (#2140).
290fn cpp_leaf_fq(
291    package_name: &str,
292    parent: Option<&CodeUnit>,
293    name: &str,
294    kind_if_nested: SegmentKind,
295    kind_if_top: SegmentKind,
296) -> FqName {
297    if let Some(parent) = parent {
298        parent
299            .fq()
300            .clone()
301            .with_pushed(segment_interner().intern(name, kind_if_nested))
302    } else {
303        let mut fq = FqName::new();
304        cpp_push_package(&mut fq, package_name);
305        fq.push(segment_interner().intern(name, kind_if_top));
306        fq
307    }
308}
309
310/// Structured name for a member unit (function, field, enumerator). The
311/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
312/// the member has an owner, by `.member`; free functions and globals have no
313/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
314/// C++ member names never contain a literal `.`, so the single `.` (if any)
315/// separates the owner chain from the member.
316pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
317    let mut fq = FqName::new();
318    cpp_push_package(&mut fq, package_name);
319    match short_name.rsplit_once('.') {
320        Some((owner_chain, member)) => {
321            cpp_push_type_chain(&mut fq, owner_chain);
322            fq.push(segment_interner().intern(member, SegmentKind::Member));
323        }
324        None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
325    }
326    fq
327}
328
329/// Structured name for a rendered C++ qualified name supplied at the input
330/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
331///
332/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
333/// it hands back one component for a nested-class chain (`Outer$Inner`). An
334/// exact-name lookup then asks the store for owner `ns` with identifier
335/// `Outer$Inner`, while the declaration persists as a
336/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
337/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
338/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
339/// boundary identical to the persisted one. A `$` that is *not* a nesting join
340/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
341/// and stays reachable through the identifier fallback, which seeks the whole
342/// component text as one spelling.
343pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
344    let mut fq = FqName::new();
345    for component in parse_symbol_path(Language::Cpp, rendered) {
346        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
347        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
348        // construction boundary `cpp_push_type_chain` owns for extraction.
349        let mut first = true;
350        for part in component.split('$').filter(|part| !part.is_empty()) {
351            let kind = if first {
352                // A client-typed head claims no kind, exactly as
353                // `parse_symbol_path_fq` records it.
354                SegmentKind::Unknown
355            } else {
356                SegmentKind::Nested
357            };
358            fq.push(segment_interner().intern(part, kind));
359            first = false;
360        }
361    }
362    fq
363}
364
365/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
366///
367/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
368/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
369/// `Top.Only`, and the exact-name retain drops the persisted nested identity
370/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
371/// placements extraction stores, so each exact query's parent and terminal
372/// match a persisted row and the structural retain keeps it.
373///
374/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
375/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
376/// is one of those: the `::` there is the package join the canonical name
377/// already spells.
378pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
379    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
380        return Vec::new();
381    }
382    let texts = parse_symbol_path(Language::Cpp, rendered);
383    if texts.len() < 2 {
384        return Vec::new();
385    }
386    cpp_qualified_kind_interpretations(&texts)
387}
388
389/// Every namespace / type / member boundary a `::`-joined source spelling can
390/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
391/// segments.
392fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
393    let len = texts.len();
394    let mut names = Vec::new();
395    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
396    for packages in 0..len {
397        let rest = len - packages;
398        let mut type_chain = Vec::with_capacity(len);
399        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
400        type_chain.push(SegmentKind::Type);
401        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
402        names.push(cpp_fq_with_kinds(texts, type_chain));
403        if rest >= 2 {
404            let mut member = Vec::with_capacity(len);
405            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
406            member.push(SegmentKind::Type);
407            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
408            member.push(SegmentKind::Member);
409            names.push(cpp_fq_with_kinds(texts, member));
410        } else if packages >= 1 {
411            let mut function = vec![SegmentKind::Package; packages];
412            function.push(SegmentKind::Member);
413            names.push(cpp_fq_with_kinds(texts, function));
414        }
415    }
416    names
417}
418
419fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
420    assert_eq!(
421        texts.len(),
422        kinds.len(),
423        "a qualified interpretation names every segment"
424    );
425    let mut fq = FqName::new();
426    for (text, kind) in texts.iter().zip(kinds) {
427        fq.push(segment_interner().intern(text, kind));
428    }
429    fq
430}
431
432#[derive(Clone)]
433pub struct ScopeInfo {
434    package_name: String,
435    module: Option<CodeUnit>,
436    class_unit: Option<CodeUnit>,
437    template_signature: Option<String>,
438    template_metadata: Option<CppTemplateMetadata>,
439    declarations_are_fields: bool,
440    recovered_specialization_member_scope: bool,
441    /// Namespace targets of every `using namespace X;` directive lexically
442    /// visible at this point in the file (declaration order), threaded
443    /// forward sibling-by-sibling by the sequential container walk (see
444    /// `CppWork::Siblings`). An out-of-line member definition written as a
445    /// bare `Class::method` at file/namespace scope with no enclosing
446    /// `namespace {}` block (issue #1093, e.g. log4cxx's
447    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
448    /// const { ... }`) has no other structural signal for which namespace
449    /// actually owns `Class`; this is the best-effort candidate list used to
450    /// recover it so the definition's indexed identity matches its header
451    /// declaration's.
452    visible_using_namespaces: Vec<String>,
453}
454
455struct CppContainer<'tree> {
456    node: Node<'tree>,
457    scope: ScopeInfo,
458}
459
460struct CppNodeWork<'tree> {
461    node: Node<'tree>,
462    scope: ScopeInfo,
463}
464
465/// Cursor over one container's remaining named children, processed one at a
466/// time (rather than all at once) so a `using namespace X;` sibling can
467/// update `scope.visible_using_namespaces` for the siblings that follow it,
468/// matching real C++ using-directive semantics. Nested container work is
469/// still pushed and fully drained before the cursor resumes (stack LIFO
470/// order), preserving the original left-to-right visitation order.
471struct CppSiblingsWork<'tree> {
472    children: std::vec::IntoIter<Node<'tree>>,
473    scope: ScopeInfo,
474}
475
476enum CppWork<'tree> {
477    Container(CppContainer<'tree>),
478    Node(CppNodeWork<'tree>),
479    Siblings(CppSiblingsWork<'tree>),
480}
481
482fn class_like_name<'tree>(
483    node: Node<'tree>,
484    source: &str,
485    ancestry: &ParentIndex<'tree>,
486) -> Option<String> {
487    let best = class_like_name_from_children(node, source);
488    if let Some(parent) = ancestry.parent(node)
489        && matches!(
490            parent.kind(),
491            "declaration" | "field_declaration" | "function_definition"
492        )
493        // A class_specifier carrying its own body proves the grammar name is
494        // the real class name: a sibling declarator then declares an object
495        // (`class X {} x;`), never a displaced class name. The gate matters
496        // when the class name is itself an all-caps token (`X`, `API`) --
497        // without it the export-macro re-read below steals the object
498        // declarator's name for the class (#2283). The genuine export-macro
499        // shapes leave the class_specifier bodyless, the same invariant
500        // recover_malformed_exported_multiple_base_class already gates on.
501        && cpp_body_node(node).is_none()
502        && node
503            .child_by_field_name("name")
504            .map(|name_node| {
505                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
506            })
507            .unwrap_or(false)
508        && let Some(recovered) = exported_class_name_from_node(parent, source)
509        && best.as_deref() != Some(recovered.as_str())
510    {
511        return Some(recovered);
512    }
513    best.or_else(|| {
514        node.child_by_field_name("name")
515            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
516            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
517    })
518}
519
520fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
521    let mut grammar_name = None;
522    if let Some(name_node) = node.child_by_field_name("name") {
523        let name = normalize_cpp_whitespace(node_text(name_node, source));
524        if name.is_empty() {
525            return None;
526        }
527        if !cpp_export_macro_token(&name) {
528            return Some(name);
529        }
530        grammar_name = Some(name);
531    }
532
533    let mut best = None;
534    let mut cursor = node.walk();
535    let mut stack = Vec::new();
536    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
537        if matches!(
538            child.kind(),
539            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
540        ) {
541            break;
542        }
543        stack.push(child);
544    }
545
546    while let Some(current) = stack.pop() {
547        if matches!(current.kind(), "type_identifier" | "identifier") {
548            let name = normalize_cpp_whitespace(node_text(current, source));
549            if !name.is_empty() && !cpp_export_macro_token(&name) {
550                best = Some(name);
551            }
552            continue;
553        }
554
555        push_named_children_reversed(current, &mut stack);
556    }
557    best.or(grammar_name)
558}
559
560pub fn cpp_export_macro_token(token: &str) -> bool {
561    token
562        .chars()
563        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
564}
565
566struct RecoveredExportedClass<'tree> {
567    declaration_node: Node<'tree>,
568    name: String,
569    body: Option<Node<'tree>>,
570    raw_supertypes: Option<Vec<String>>,
571    base_facts: Option<Vec<CppBaseSpecifierFact>>,
572    uses_initializer_body: bool,
573    /// Present only for the fragmented multiple-base export shape (issue #938).
574    /// Carries the true class-body byte region -- the members tree-sitter scattered
575    /// out of the recovered node -- so they can be reparsed and re-owned as members
576    /// rather than lost inside the truncated `initializer_list` stand-in.
577    fragmented_body: Option<FragmentedExportBody>,
578}
579
580struct RecoveredFunctionLikeExportClassPair {
581    name: String,
582    range: Range,
583    raw_supertypes: Option<Vec<String>>,
584    base_facts: Option<Vec<CppBaseSpecifierFact>>,
585    fragmented_body: FragmentedExportBody,
586}
587
588struct RecoveredEmbeddedFunctionLikeExportClass {
589    name: String,
590    range: Range,
591    raw_supertypes: Vec<String>,
592    base_facts: Vec<CppBaseSpecifierFact>,
593    fragmented_body: FragmentedExportBody,
594}
595
596struct RecoveredFunctionLikeExportClass {
597    name: String,
598    range: Range,
599    raw_supertypes: Option<Vec<String>>,
600    base_facts: Option<Vec<CppBaseSpecifierFact>>,
601    member_outcome: Option<FragmentedExportMembers>,
602}
603
604/// The recovered class-body geometry for a fragmented multiple-base export class.
605/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
606/// verbatim for a region reparse (issue #941 machinery) so every recovered member
607/// keeps its exact original byte/line position. `class_range` is the full class
608/// navigation range spanning to the displaced closing brace.
609struct FragmentedExportBody {
610    reparse_start: usize,
611    reparse_end: usize,
612    class_range: Range,
613}
614
615fn recovered_fragmented_export_body(
616    body: Node<'_>,
617    class_range: Range,
618) -> Option<FragmentedExportBody> {
619    let open = body.child(0).filter(|child| child.kind() == "{")?;
620    let close = body
621        .child(body.child_count().saturating_sub(1))
622        .filter(|child| child.kind() == "}" && !child.is_missing());
623    Some(FragmentedExportBody {
624        reparse_start: open.end_byte(),
625        // A zero-width missing `}` contributes no source byte. Keep the whole
626        // body range in that case; subtracting one byte would discard the last
627        // member's semicolon and make the otherwise valid region unsafe to
628        // index. A real close token is excluded by its structured start.
629        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
630        class_range,
631    })
632}
633
634struct DisplacedFragmentNamespaceBoundary<'tree> {
635    class_close: Node<'tree>,
636    class_semicolon: Node<'tree>,
637    namespace_items: Vec<Node<'tree>>,
638}
639
640/// Result of validating a reparsed fragmented class body.  A complete tree can
641/// safely consume the whole region.  A partial tree may contain only the exact
642/// class-named constructor that tree-sitter merged into an access label; its
643/// remaining siblings must stay on the ordinary outer walk.
644enum FragmentedExportMembers {
645    Complete(Tree),
646    ConditionalConstructor(Tree),
647}
648
649#[derive(Clone, Copy)]
650struct DisplacedMacroClassTail {
651    split_index: usize,
652    class_range: Range,
653}
654
655fn recover_exported_class_declaration<'tree>(
656    node: Node<'tree>,
657    source: &str,
658) -> Option<RecoveredExportedClass<'tree>> {
659    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
660        return Some(recovered);
661    }
662
663    let class_node = first_class_like_child(node)?;
664    if let Some(name_node) = class_node.child_by_field_name("name") {
665        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
666        if cpp_export_macro_token(&class_name) {
667            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
668            // Name declarator. Only a bare declarator can be the displaced class name;
669            // wrappers describe an object whose type merely happens to look macro-like.
670            let mut cursor = node.walk();
671            if node
672                .children_by_field_name("declarator", &mut cursor)
673                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
674            {
675                return None;
676            }
677        } else if has_direct_cpp_declarator(node) {
678            return None;
679        }
680    }
681    let name = exported_class_name_from_node(class_node, source)?;
682    Some(RecoveredExportedClass {
683        declaration_node: class_node,
684        name,
685        body: cpp_body_node(class_node),
686        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
687            .then(|| extract_cpp_supertypes(class_node, source)),
688        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
689            .then(|| extract_cpp_base_facts(class_node, source)),
690        uses_initializer_body: false,
691        fragmented_body: None,
692    })
693}
694
695fn recover_malformed_exported_base_class<'tree>(
696    node: Node<'tree>,
697    source: &str,
698) -> Option<RecoveredExportedClass<'tree>> {
699    if node.kind() != "declaration" {
700        return None;
701    }
702    let class_node = node.child_by_field_name("type")?;
703    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
704        return None;
705    }
706    let macro_name = class_node
707        .child_by_field_name("name")
708        .and_then(|name| direct_identifier_name(name, source))?;
709    if !cpp_export_macro_token(&macro_name) {
710        return None;
711    }
712
713    let mut named_cursor = node.walk();
714    let mut named = node.named_children(&mut named_cursor);
715    // Object-like macro lines before the head are provenance modifiers under
716    // the macro-declaration grammar; the class node follows them.
717    let head_matches_class = loop {
718        let child = named.next()?;
719        if child.kind() == "macro_modifier" {
720            continue;
721        }
722        break same_node(child, class_node);
723    };
724    if !head_matches_class {
725        return None;
726    }
727    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
728    if displaced.kind() == "parenthesized_declarator" {
729        // The macro-declaration grammar keeps the invocation, the displaced
730        // class name and the first base inside a `parenthesized_declarator`,
731        // and the remaining bases beside the body in the trailing
732        // `init_declarator`.
733        let (name, first_base_node) =
734            recovered_function_like_export_class_owner_node(displaced, source)?;
735        let remaining = named.collect::<Vec<_>>();
736        let [init] = remaining.as_slice() else {
737            return None;
738        };
739        if init.kind() != "init_declarator" {
740            return None;
741        }
742        let mut final_base_node = init.child_by_field_name("declarator")?;
743        while final_base_node.kind() == "macro_modifier_declarator" {
744            final_base_node = final_base_node.child_by_field_name("declarator")?;
745        }
746        let first_base = recovered_malformed_base_name(first_base_node, source)?;
747        let final_base = recovered_malformed_base_name(final_base_node, source)?;
748        let body = init.child_by_field_name("value")?;
749        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
750            return None;
751        }
752        return Some(RecoveredExportedClass {
753            declaration_node: node,
754            name,
755            body: Some(body),
756            raw_supertypes: Some(vec![first_base, final_base]),
757            base_facts: Some(vec![
758                recovered_base_specifier(
759                    first_base_node,
760                    source,
761                    recovered_base_is_virtual(displaced, first_base_node, source),
762                )?,
763                recovered_base_specifier(
764                    final_base_node,
765                    source,
766                    recovered_base_is_virtual(*init, final_base_node, source),
767                )?,
768            ]),
769            uses_initializer_body: true,
770            fragmented_body: fragmented_export_body_region(node, body, source),
771        });
772    }
773    if displaced.kind() != "ERROR" {
774        return None;
775    }
776    let name = displaced_exported_class_name(displaced, source)?;
777
778    let remaining = named.collect::<Vec<_>>();
779    let init = *remaining.last()?;
780    if init.kind() != "init_declarator" {
781        return None;
782    }
783    let final_base = init
784        .child_by_field_name("declarator")
785        .and_then(|base| recovered_malformed_base_name(base, source))?;
786    let body = init.child_by_field_name("value")?;
787    // A complete reduction has a real closing brace here. In Chromium's Widget
788    // declaration, tree-sitter instead emits the same direct `}` slot as a
789    // zero-width missing node where the first body macro truncates the prefix.
790    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
791        return None;
792    }
793
794    if remaining[..remaining.len() - 1]
795        .iter()
796        .any(|child| match child.kind() {
797            "qualified_identifier"
798            | "scoped_type_identifier"
799            | "type_identifier"
800            | "identifier"
801            | "macro_modifier_declarator" => false,
802            "ERROR" => !is_malformed_inheritance_access(*child, source),
803            _ => true,
804        })
805    {
806        return None;
807    }
808
809    let mut raw_supertypes = Vec::new();
810    let mut base_facts = Vec::new();
811    for base in &remaining[..remaining.len() - 1] {
812        if base.kind() == "ERROR" {
813            continue;
814        }
815        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
816        base_facts.push(recovered_base_specifier(
817            *base,
818            source,
819            recovered_base_is_virtual(node, *base, source),
820        )?);
821    }
822    raw_supertypes.push(final_base);
823    let final_base_node = init.child_by_field_name("declarator")?;
824    base_facts.push(recovered_base_specifier(
825        final_base_node,
826        source,
827        recovered_base_is_virtual(init, final_base_node, source),
828    )?);
829
830    Some(RecoveredExportedClass {
831        declaration_node: node,
832        name,
833        body: Some(body),
834        raw_supertypes: Some(raw_supertypes),
835        base_facts: Some(base_facts),
836        uses_initializer_body: true,
837        fragmented_body: fragmented_export_body_region(node, body, source),
838    })
839}
840
841/// Locate the true class-body region for a fragmented multiple-base export class.
842///
843/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
844/// emits in place of the real class body. Tree-sitter reduces that body in one of
845/// two shapes, both of which lose the members from the recovered node:
846///
847/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
848///   a real closing brace and holds the whole body text inline. The interior between
849///   the braces reparses to the members directly.
850/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
851///   first member with a zero-width MISSING `}`; every later member -- and the real
852///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
853///   siblings. The interior runs from the opening brace to that displaced `}`.
854///
855/// Returns the interior byte range to reparse plus the full class navigation range.
856fn fragmented_export_body_region(
857    node: Node<'_>,
858    body: Node<'_>,
859    source: &str,
860) -> Option<FragmentedExportBody> {
861    let reparse_start = body.start_byte() + 1;
862    let close = direct_close_brace(body)?;
863    if close.end_byte() > close.start_byte() {
864        return Some(FragmentedExportBody {
865            reparse_start,
866            reparse_end: close.start_byte(),
867            class_range: cpp_declaration_range(node),
868        });
869    }
870    // The closing brace was displaced past the recovered node. A balanced nested
871    // class keeps its own braces, so the first lone-`}` sibling is this class's.
872    let mut sibling = node.next_named_sibling();
873    let displaced_close = loop {
874        let Some(current) = sibling else {
875            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
876        };
877        if cpp_is_stray_close_brace(current, source) {
878            break current;
879        }
880        sibling = current.next_named_sibling();
881    };
882    Some(FragmentedExportBody {
883        reparse_start,
884        reparse_end: displaced_close.start_byte(),
885        class_range: Range {
886            start_byte: node.start_byte(),
887            end_byte: displaced_close.end_byte(),
888            start_line: node.start_position().row + 1,
889            end_line: displaced_close.end_position().row + 1,
890        },
891    })
892}
893
894/// Locate the true class-body region for the export-macro class shape that
895/// tree-sitter promotes to a `function_definition`.
896///
897/// In this shape the synthetic function body closes at the first inline
898/// method, while the class's real members continue as root-level siblings until
899/// a stray `}` followed by the displaced class `;`. Reparse the complete
900/// interior so those siblings are visited with the recovered class scope.
901fn fragmented_export_function_body_region(
902    node: Node<'_>,
903    body: Node<'_>,
904    source: &str,
905    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
906) -> Option<FragmentedExportBody> {
907    let reparse_start = body.start_byte().checked_add(1)?;
908    if let Some(boundary) = displaced_namespace {
909        return Some(FragmentedExportBody {
910            reparse_start,
911            reparse_end: boundary.class_close.start_byte(),
912            class_range: Range {
913                start_byte: node.start_byte(),
914                end_byte: boundary.class_semicolon.end_byte(),
915                start_line: node.start_position().row + 1,
916                end_line: boundary.class_semicolon.end_position().row + 1,
917            },
918        });
919    }
920    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
921    let boundary =
922        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
923    let boundary_index = boundary.and_then(|boundary| {
924        siblings
925            .iter()
926            .position(|candidate| same_node(*candidate, boundary))
927    });
928    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
929    let mut sibling_index = 0;
930    // A complete recovered class's synthetic wrapper is immediately followed
931    // by its displaced semicolon (comments and a trailing attribute macro --
932    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
933    // may sit between the body and that semicolon). Only scan for a later
934    // stray close when real member siblings intervene; otherwise every earlier
935    // complete class would borrow the next malformed class's close and claim
936    // its members. The trailing-attribute case is the gtest shape: the scan
937    // borrowed a close ~1900 lines later and re-owned a following
938    // `namespace testing { namespace internal {` block as class members,
939    // doubling the package path ("testing::internal::testing::internal") and
940    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
941    // boundary assert (#2297).
942    while let Some(current) = siblings.get(sibling_index).copied() {
943        if current.kind() == "comment" {
944            sibling_index += 1;
945            continue;
946        }
947        if is_trailing_attribute_macro_sibling(current) {
948            sibling_index += 1;
949            continue;
950        }
951        if cpp_is_stray_semicolon(current, source) {
952            return None;
953        }
954        break;
955    }
956    while let Some(current) = siblings.get(sibling_index).copied() {
957        let next = siblings.get(sibling_index + 1).copied();
958        if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
959            return Some(FragmentedExportBody {
960                reparse_start,
961                reparse_end: current.start_byte(),
962                class_range: Range {
963                    start_byte: node.start_byte(),
964                    end_byte: semicolon.end_byte(),
965                    start_line: node.start_position().row + 1,
966                    end_line: semicolon.end_position().row + 1,
967                },
968            });
969        }
970        // When the final access label keeps the class close in its malformed
971        // declaration body, tree-sitter nests the lone `}` ERROR below the
972        // label instead of exposing it as a direct sibling. Search only the
973        // scattered siblings after the synthetic wrapper. The first such
974        // close is the class terminator because nested class bodies retain
975        // their own balanced class_specifier nodes.
976        if current.start_byte() >= body.end_byte()
977            && let Some(close) = cpp_nested_stray_close_brace(current, source)
978        {
979            return Some(FragmentedExportBody {
980                reparse_start,
981                reparse_end: close.start_byte(),
982                class_range: Range {
983                    start_byte: node.start_byte(),
984                    end_byte: current.end_byte(),
985                    start_line: node.start_position().row + 1,
986                    end_line: current.end_position().row + 1,
987                },
988            });
989        }
990        sibling_index += 1;
991    }
992    boundary.map(|boundary| FragmentedExportBody {
993        reparse_start,
994        reparse_end: boundary.start_byte(),
995        class_range: Range {
996            start_byte: node.start_byte(),
997            end_byte: boundary.start_byte(),
998            start_line: node.start_position().row + 1,
999            end_line: boundary.start_position().row + 1,
1000        },
1001    })
1002}
1003
1004/// Find a later macro-export class that tree-sitter lifted through an enclosing
1005/// preprocessor container. A class that is still a direct sibling can be a
1006/// nested member of the current fragmented class, so only a changed parent is
1007/// a proven boundary between the two recovered class envelopes.
1008fn fragmented_export_sibling_class_boundary<'tree>(
1009    node: Node<'tree>,
1010    source: &str,
1011    ancestry: &ParentIndex<'tree>,
1012) -> Option<Node<'tree>> {
1013    let node_parent = ancestry.parent(node)?;
1014    cpp_following_named_siblings(node, source, ancestry)
1015        .into_iter()
1016        .find(|candidate| {
1017            recover_exported_class_function_definition(*candidate, source).is_some()
1018                && ancestry
1019                    .parent(*candidate)
1020                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1021        })
1022}
1023
1024/// A trailing attribute macro after a recovered class's closing brace, spelled
1025/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
1026/// bare identifier is never a class member (members need a type), so this
1027/// sibling can only be the class's own tail (#2297).
1028fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1029    if node.kind() != "expression_statement" {
1030        return false;
1031    }
1032    let mut cursor = node.walk();
1033    let mut children = node.named_children(&mut cursor);
1034    children
1035        .next()
1036        .is_some_and(|child| child.kind() == "identifier")
1037        && children.next().is_none()
1038}
1039
1040/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
1041/// export-class wrapper can place the class close inside an access-label node,
1042/// so direct-sibling checks alone miss the boundary.  Walk named CST children
1043/// only; the helper does not inspect source text beyond the existing structured
1044/// stray-brace predicate.
1045fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1046    let mut stack = vec![node];
1047    while let Some(current) = stack.pop() {
1048        if cpp_is_stray_close_brace(current, source) {
1049            return Some(current);
1050        }
1051        let mut cursor = current.walk();
1052        stack.extend(current.named_children(&mut cursor));
1053    }
1054    None
1055}
1056
1057/// Return named siblings that follow `node`, including siblings that tree-sitter
1058/// attached to an enclosing container after malformed recovery split the local
1059/// declaration list. Stop at the first structurally visible class close so a
1060/// later namespace or exported class cannot supply the recovery boundary.
1061///
1062/// The climb takes a [`ParentIndex`] because it runs once per visited node and
1063/// walks every level to the root: with `Node::parent`, which re-descends from
1064/// the root on each call, a translation unit holding thousands of siblings --
1065/// generated instruction tables, for instance -- made this the whole cost of
1066/// indexing the file. Callers inside the declaration walk pass the walk's
1067/// index; the four that ask from outside a traversal pass
1068/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
1069/// behaviour is unchanged.
1070fn cpp_following_named_siblings<'tree>(
1071    node: Node<'tree>,
1072    source: &str,
1073    ancestry: &ParentIndex<'tree>,
1074) -> Vec<Node<'tree>> {
1075    let mut siblings = Vec::new();
1076    let mut anchor = node;
1077    while let Some(parent) = ancestry.parent(anchor) {
1078        let at_translation_unit = parent.kind() == "translation_unit";
1079        let mut cursor = parent.walk();
1080        assert!(
1081            cursor.goto_first_child(),
1082            "ParentIndex returned a parent without children"
1083        );
1084        while !same_node(cursor.node(), anchor) {
1085            assert!(
1086                cursor.goto_next_sibling(),
1087                "ParentIndex parent does not contain its child anchor"
1088            );
1089        }
1090        while cursor.goto_next_sibling() {
1091            let current = cursor.node();
1092            if !current.is_named() {
1093                continue;
1094            }
1095            if at_translation_unit
1096                && (current.kind() == "namespace_definition"
1097                    || (current.kind() == "function_definition"
1098                        && first_class_like_child(current).is_some()))
1099            {
1100                return siblings;
1101            }
1102            siblings.push(current);
1103            if cpp_is_stray_close_brace(current, source) {
1104                if let Some(semicolon) = current
1105                    .next_named_sibling()
1106                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1107                {
1108                    siblings.push(semicolon);
1109                }
1110                return siblings;
1111            }
1112            if current.start_byte() >= node.end_byte()
1113                && matches!(current.kind(), "ERROR" | "labeled_statement")
1114                && cpp_nested_stray_close_brace(current, source).is_some()
1115            {
1116                return siblings;
1117            }
1118        }
1119        anchor = parent;
1120    }
1121    siblings
1122}
1123
1124fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1125    if node.start_byte() >= class_end {
1126        return false;
1127    }
1128    node.end_byte() <= class_end
1129        || cpp_nested_stray_close_brace(node, source)
1130            .is_some_and(|close| close.start_byte() == class_end)
1131}
1132
1133/// A class whose head tree-sitter kept in one malformed container while the
1134/// members after the first and the closing `};` scattered across that
1135/// container's following siblings.
1136struct FragmentedClassRecovery<'tree> {
1137    declaration_node: Node<'tree>,
1138    name: String,
1139    raw_supertypes: Vec<String>,
1140    body: FragmentedExportBody,
1141}
1142
1143/// Recover a class whose opening prefix is retained in one ERROR node while one
1144/// or more nested class closes and the outer close are displaced to sibling
1145/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1146/// tokens establish nesting depth and the displaced close nodes terminate it.
1147///
1148/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1149/// `class` keyword as the container's first token. A function-like export macro
1150/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1151/// body-less `class_specifier` named after the macro followed by the
1152/// invocation's `(`, arguments and `)`, with the real class name and the base
1153/// clause left as bare tokens after it (#2924).
1154fn fragmented_class_body<'tree>(
1155    node: Node<'tree>,
1156    source: &str,
1157) -> Option<FragmentedClassRecovery<'tree>> {
1158    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1159        return Some(recovered);
1160    }
1161    let supported_container = node.kind() == "ERROR"
1162        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1163    if !supported_container {
1164        return None;
1165    }
1166    let mut cursor = node.walk();
1167    let children = node.children(&mut cursor).collect::<Vec<_>>();
1168    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1169        return Some(recovered);
1170    }
1171    let keyword = children.first()?;
1172    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1173        return None;
1174    }
1175    let name_node = children
1176        .iter()
1177        .copied()
1178        .skip(1)
1179        .find(|child| child.is_named())?;
1180    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1181        return None;
1182    }
1183    let name = normalize_cpp_whitespace(node_text(name_node, source));
1184    if name.is_empty() || cpp_export_macro_token(&name) {
1185        return None;
1186    }
1187    let open_index = children.iter().position(|child| child.kind() == "{")?;
1188    let open = children[open_index];
1189    let body = displaced_fragment_namespace_geometry(node, source)
1190        .map(|boundary| FragmentedExportBody {
1191            reparse_start: open.end_byte(),
1192            reparse_end: boundary.class_close.start_byte(),
1193            class_range: Range {
1194                start_byte: node.start_byte(),
1195                end_byte: boundary.class_semicolon.end_byte(),
1196                start_line: node.start_position().row + 1,
1197                end_line: boundary.class_semicolon.end_position().row + 1,
1198            },
1199        })
1200        .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1201    Some(FragmentedClassRecovery {
1202        declaration_node: node,
1203        name,
1204        raw_supertypes: extract_cpp_supertypes(node, source),
1205        body,
1206    })
1207}
1208
1209/// The fragmented body of a function-like export-macro class head that
1210/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1211/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1212/// public virtual EC_PublicKey {`, #2924).
1213///
1214/// The class name is read positionally by [`recovered_export_head_name`] -- the
1215/// last identifier before the head ends at `final`, at the base clause `:`, or
1216/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1217/// spelled with `virtual`, with `final`, or with qualified base names needs no
1218/// arm of its own.
1219fn fragmented_export_macro_class_body<'tree>(
1220    node: Node<'tree>,
1221    children: &[Node<'tree>],
1222    source: &str,
1223) -> Option<FragmentedClassRecovery<'tree>> {
1224    let class_node = *children.first()?;
1225    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1226        return None;
1227    }
1228    // The identifier tree-sitter took for the class name is the export macro, a
1229    // function-like invocation when the `(` of its argument list follows it
1230    // directly. Spelling plays no part (#2557).
1231    class_node
1232        .child_by_field_name("name")
1233        .and_then(|name| direct_identifier_name(name, source))?;
1234    let invocation = *children.get(1)?;
1235    if invocation.is_named() || invocation.kind() != "(" {
1236        return None;
1237    }
1238    let open_index = children
1239        .iter()
1240        .position(|child| !child.is_named() && child.kind() == "{")?;
1241    let open = children[open_index];
1242    let name_node = recovered_export_head_name(node, open, source)?;
1243    let name = normalize_cpp_whitespace(node_text(name_node, source));
1244    if name.is_empty() || cpp_export_macro_token(&name) {
1245        return None;
1246    }
1247    Some(FragmentedClassRecovery {
1248        declaration_node: node,
1249        name,
1250        raw_supertypes: recovered_export_head_bases(
1251            node,
1252            name_node.end_byte(),
1253            open.start_byte(),
1254            source,
1255        ),
1256        body: fragmented_displaced_class_body(node, children, open_index, source)
1257            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1258    })
1259}
1260
1261/// The class body a fragmented head opened at `children[open_index]` but could
1262/// not close: tree-sitter scattered the remaining members and the closing `};`
1263/// across the container's following siblings. The direct class tokens after the
1264/// opening brace say how many closes the nested classes consume before this
1265/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1266fn fragmented_displaced_class_body(
1267    node: Node<'_>,
1268    children: &[Node<'_>],
1269    open_index: usize,
1270    source: &str,
1271) -> Option<FragmentedExportBody> {
1272    let open = children[open_index];
1273    let nested_class_opens = children[open_index + 1..]
1274        .iter()
1275        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1276        .count();
1277    let mut closes_remaining = 1 + nested_class_opens;
1278    let mut sibling = node.next_named_sibling();
1279    while let Some(candidate) = sibling {
1280        let next = candidate.next_named_sibling();
1281        // The class's own close: a lone `}` `ERROR` the mis-parse split off,
1282        // or -- under the macro-declaration grammar, which keeps the fragmented
1283        // head in an `ERROR` and the close as a plain child of the enclosing
1284        // list -- a bare `}` there. The `;` after it is what proves the close
1285        // is the class's own rather than the enclosing scope's.
1286        let is_close = cpp_is_stray_close_brace(candidate, source)
1287            || (candidate.kind() == "}" && !candidate.is_named());
1288        if is_close {
1289            closes_remaining -= 1;
1290            if closes_remaining == 0 {
1291                let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1292                if open.end_byte() >= candidate.start_byte() {
1293                    return None;
1294                }
1295                return Some(FragmentedExportBody {
1296                    reparse_start: open.end_byte(),
1297                    reparse_end: candidate.start_byte(),
1298                    class_range: Range {
1299                        start_byte: node.start_byte(),
1300                        end_byte: semicolon.end_byte(),
1301                        start_line: node.start_position().row + 1,
1302                        end_line: semicolon.end_position().row + 1,
1303                    },
1304                });
1305            }
1306        }
1307        sibling = next;
1308    }
1309    None
1310}
1311
1312/// The body of a fragmented export-macro class head whose close tree-sitter
1313/// spent on the enclosing container instead: Botan's
1314/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1315/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1316/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1317/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1318/// (#2924).
1319fn fragmented_container_close_class_body(
1320    node: Node<'_>,
1321    open: Node<'_>,
1322    source: &str,
1323) -> Option<FragmentedExportBody> {
1324    let parent = node.parent()?;
1325    if !matches!(
1326        parent.kind(),
1327        "declaration_list" | "field_declaration_list" | "compound_statement"
1328    ) {
1329        return None;
1330    }
1331    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1332    // The container's close is this class's close only when the body's own
1333    // brace balance says so. Without that cross-check -- the one issue #1524
1334    // already uses on a mis-closed wrapper body -- a class that really does end
1335    // earlier would claim every later container-level declaration as a member.
1336    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1337        || open.end_byte() >= close.start_byte()
1338    {
1339        return None;
1340    }
1341    Some(FragmentedExportBody {
1342        reparse_start: open.end_byte(),
1343        reparse_end: close.start_byte(),
1344        class_range: Range {
1345            start_byte: node.start_byte(),
1346            end_byte: close.end_byte(),
1347            start_line: node.start_position().row + 1,
1348            end_line: close.end_position().row + 1,
1349        },
1350    })
1351}
1352
1353pub(crate) fn recovered_fragmented_class_has_body(
1354    node: Node<'_>,
1355    source: &str,
1356    expected_name: &str,
1357    expected_range: &Range,
1358) -> bool {
1359    fragmented_class_body(node, source).is_some_and(|recovered| {
1360        recovered.name == expected_name
1361            && recovered.body.class_range.start_byte == expected_range.start_byte
1362            && recovered.body.class_range.end_byte == expected_range.end_byte
1363    })
1364}
1365
1366/// Recover a plain class whose parser-visible body ends inside a malformed
1367/// inline member. Tree-sitter then attaches either the next real member
1368/// declarator or the unfinished `else` branch directly to the outer function
1369/// definition and leaves the class's actual `};` among later siblings. Those
1370/// structured continuations and the close/semicolon siblings establish the
1371/// complete body envelope without interpreting source text.
1372fn fragmented_plain_class_declaration_body<'tree>(
1373    node: Node<'tree>,
1374    source: &str,
1375) -> Option<FragmentedClassRecovery<'tree>> {
1376    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1377        return None;
1378    }
1379    let class_node = node.child_by_field_name("type")?;
1380    if !matches!(
1381        class_node.kind(),
1382        "class_specifier" | "struct_specifier" | "union_specifier"
1383    ) {
1384        return None;
1385    }
1386    let name_node = class_node.child_by_field_name("name")?;
1387    let name = normalize_cpp_whitespace(node_text(name_node, source));
1388    if name.is_empty() || cpp_export_macro_token(&name) {
1389        return None;
1390    }
1391    let body = cpp_body_node(class_node)?;
1392    if body.kind() != "field_declaration_list" {
1393        return None;
1394    }
1395    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1396        if declarator.start_byte() < class_node.end_byte() {
1397            return None;
1398        }
1399        let mut cursor = node.walk();
1400        node.named_children(&mut cursor).any(|child| {
1401            if child.kind() != "ERROR"
1402                || child.start_byte() < class_node.end_byte()
1403                || child.end_byte() > declarator.start_byte()
1404            {
1405                return false;
1406            }
1407            let mut cursor = child.walk();
1408            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1409            let Some((return_type, attributes)) = components.split_last() else {
1410                return false;
1411            };
1412            matches!(
1413                return_type.kind(),
1414                "identifier"
1415                    | "type_identifier"
1416                    | "primitive_type"
1417                    | "decltype"
1418                    | "placeholder_type_specifier"
1419            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1420                && attributes.iter().all(|attribute| {
1421                    matches!(attribute.kind(), "identifier" | "type_identifier")
1422                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1423                            *attribute, source,
1424                        )))
1425                })
1426        })
1427    } else {
1428        let mut cursor = node.walk();
1429        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1430        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1431            if same_node(*candidate_class, class_node)
1432                && continuation.kind() == "identifier"
1433                && node_text(*continuation, source) == "else"
1434                && continuation_body.kind() == "compound_statement"
1435                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1436                && continuation_body
1437                    .child(continuation_body.child_count().saturating_sub(1))
1438                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1439    };
1440    if !displaced_member {
1441        return None;
1442    }
1443    let open = body
1444        .children(&mut body.walk())
1445        .find(|child| child.kind() == "{")?;
1446    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1447    let ordinary_boundary =
1448        siblings
1449            .iter()
1450            .copied()
1451            .enumerate()
1452            .find_map(|(close_index, close)| {
1453                cpp_stray_close_followed_by_semicolon(
1454                    close,
1455                    siblings.get(close_index + 1).copied(),
1456                    source,
1457                )
1458                .map(|semicolon| (close, semicolon))
1459            });
1460    let (close, semicolon) =
1461        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1462            (boundary.class_close, boundary.class_semicolon)
1463        } else {
1464            ordinary_boundary?
1465        };
1466    if open.end_byte() >= close.start_byte() {
1467        return None;
1468    }
1469    Some(FragmentedClassRecovery {
1470        declaration_node: class_node,
1471        name,
1472        raw_supertypes: extract_cpp_supertypes(class_node, source),
1473        body: FragmentedExportBody {
1474            reparse_start: open.end_byte(),
1475            reparse_end: close.start_byte(),
1476            class_range: Range {
1477                start_byte: class_node.start_byte(),
1478                end_byte: semicolon.end_byte(),
1479                start_line: class_node.start_position().row + 1,
1480                end_line: semicolon.end_position().row + 1,
1481            },
1482        },
1483    })
1484}
1485
1486fn displaced_export_function_namespace_shape<'tree>(
1487    declaration: Node<'tree>,
1488    source: &str,
1489) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1490    let mut nested = Vec::new();
1491    for index in (0..declaration.named_child_count()).rev() {
1492        nested.push(declaration.named_child(index)?);
1493    }
1494    while let Some(current) = nested.pop() {
1495        // A recovered export class nested in this class can consume the first
1496        // parser-visible namespace close itself. In that shape the existing
1497        // later-class boundary logic already distinguishes the nested and
1498        // namespace-sibling owners; do not mistake the nested close for this
1499        // class's terminator.
1500        if recover_exported_class_function_definition(current, source).is_some() {
1501            return None;
1502        }
1503        for index in (0..current.named_child_count()).rev() {
1504            nested.push(current.named_child(index)?);
1505        }
1506    }
1507    let mut same_envelope_sibling = declaration.next_named_sibling();
1508    while let Some(current) = same_envelope_sibling {
1509        if recover_exported_class_function_definition(current, source).is_some() {
1510            return None;
1511        }
1512        same_envelope_sibling = current.next_named_sibling();
1513    }
1514    let declaration_list = declaration.parent()?;
1515    let (namespace, class_close, trailing_semicolon) =
1516        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1517    // A chain of malformed export classes can consume one parser-visible
1518    // namespace close per class. Walk through the enclosing sibling levels so
1519    // the later real namespace close remains the structural boundary; a
1520    // direct next-sibling walk stops at the first collapsed namespace and
1521    // incorrectly makes its intervening items members of this class.
1522    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1523    let trailing_index = siblings
1524        .iter()
1525        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1526    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1527        recover_exported_class_function_definition(*candidate, source).is_some()
1528    }) {
1529        // Consecutive recovered classes already have an exact sibling-class
1530        // boundary. Preserve that established path, including nested export
1531        // classes, instead of interpreting the first class close as a
1532        // collapsed namespace boundary.
1533        return None;
1534    }
1535    let mut namespace_items = Vec::new();
1536    let mut nested_fragment_end = 0;
1537    for current in siblings.into_iter().skip(trailing_index + 1) {
1538        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1539        {
1540            return Some(DisplacedFragmentNamespaceBoundary {
1541                class_close,
1542                class_semicolon: trailing_semicolon,
1543                namespace_items,
1544            });
1545        }
1546        if current.start_byte() >= nested_fragment_end
1547            && let Some(recovered) = fragmented_class_body(current, source)
1548        {
1549            nested_fragment_end = recovered.body.class_range.end_byte;
1550        } else if current.start_byte() >= nested_fragment_end
1551            && recover_exported_class_function_definition(current, source).is_some()
1552            && let Some(body) = cpp_body_node(current)
1553            && let Some(fragmented) =
1554                fragmented_export_function_body_region(current, body, source, None)
1555        {
1556            nested_fragment_end = fragmented.class_range.end_byte;
1557        }
1558        namespace_items.push(current);
1559    }
1560    None
1561}
1562
1563fn displaced_fragment_namespace_boundary<'tree>(
1564    declaration: Node<'tree>,
1565    body: Node<'tree>,
1566    source: &str,
1567) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1568    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1569    let reparse_start = body.start_byte() + 1;
1570    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1571    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1572}
1573
1574/// Recover the class/namespace brace geometry for a declaration whose class
1575/// close tree-sitter consumed as the enclosing namespace close. This proof is
1576/// independent of whether every member in the class body can be reparsed: the
1577/// ordinary-tree fallback can still re-own bounded sibling declarations when
1578/// an unknown macro makes the complete body reparse unsafe.
1579fn displaced_fragment_namespace_geometry<'tree>(
1580    declaration: Node<'tree>,
1581    source: &str,
1582) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1583    // A templated class's malformed function wrapper remains beneath the
1584    // template node even though its later members have escaped to the
1585    // enclosing declaration list. Lift only that exact declaration child.
1586    let envelope = declaration
1587        .parent()
1588        .filter(|parent| {
1589            parent.kind() == "template_declaration"
1590                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1591        })
1592        .unwrap_or(declaration);
1593    let declaration_list = envelope.parent()?;
1594    let (_, class_close, trailing_semicolon) =
1595        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1596    let mut namespace_items = Vec::new();
1597    let mut sibling = trailing_semicolon.next_named_sibling();
1598    let mut nested_fragment_end = 0;
1599    loop {
1600        let current = sibling?;
1601        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1602        {
1603            break;
1604        }
1605        if current.start_byte() >= nested_fragment_end
1606            && let Some(recovered) = fragmented_class_body(current, source)
1607        {
1608            nested_fragment_end = recovered.body.class_range.end_byte;
1609        }
1610        namespace_items.push(current);
1611        sibling = current.next_named_sibling();
1612    }
1613    Some(DisplacedFragmentNamespaceBoundary {
1614        class_close,
1615        class_semicolon: trailing_semicolon,
1616        namespace_items,
1617    })
1618}
1619
1620fn cpp_fragment_namespace_boundary_nodes<'tree>(
1621    declaration_list: Node<'tree>,
1622) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1623    if declaration_list.kind() != "declaration_list" {
1624        return None;
1625    }
1626    let namespace = declaration_list.parent()?;
1627    if namespace.kind() != "namespace_definition"
1628        || namespace.child_by_field_name("body") != Some(declaration_list)
1629    {
1630        return None;
1631    }
1632    let class_close = direct_close_brace(declaration_list)?;
1633    let trailing_semicolon = namespace.next_named_sibling()?;
1634    (trailing_semicolon.kind() == "expression_statement"
1635        && trailing_semicolon.named_child_count() == 0)
1636        .then_some((namespace, class_close, trailing_semicolon))
1637}
1638
1639/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1640fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1641    (0..node.child_count())
1642        .filter_map(|index| node.child(index))
1643        .find(|child| !child.is_named() && child.kind() == "}")
1644}
1645
1646/// A displaced lone closing brace: the class close that the fragmented multiple-base
1647/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1648fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1649    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1650}
1651
1652/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1653/// text while skipping line/block comments and string/char literals. The
1654/// exported-class recovery needs this when tree-sitter's bogus
1655/// `function_definition` body runs past the class's true closing brace and
1656/// swallows following siblings (issue #1524): the grammar tree carries no
1657/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1658/// close is located textually. Returns `None` when the text is unbalanced or
1659/// contains a construct the scanner deliberately does not interpret (raw
1660/// strings) -- callers treat that as "cannot partition" and keep the
1661/// un-split recovery.
1662fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1663    let bytes = source.as_bytes();
1664    if bytes.get(open_byte) != Some(&b'{') {
1665        return None;
1666    }
1667    let mut depth = 0usize;
1668    let mut i = open_byte;
1669    while i < bytes.len() {
1670        match bytes[i] {
1671            b'{' => depth += 1,
1672            b'}' => {
1673                depth = depth.checked_sub(1)?;
1674                if depth == 0 {
1675                    return Some(i);
1676                }
1677            }
1678            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1679                while i < bytes.len() && bytes[i] != b'\n' {
1680                    i += 1;
1681                }
1682                continue;
1683            }
1684            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1685                i += 2;
1686                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1687                    i += 1;
1688                }
1689                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1690                continue;
1691            }
1692            quote @ (b'"' | b'\'') => {
1693                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1694                // bail out rather than mis-count.
1695                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1696                    return None;
1697                }
1698                i += 1;
1699                while i < bytes.len() && bytes[i] != quote {
1700                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1701                }
1702                if i >= bytes.len() {
1703                    return None;
1704                }
1705            }
1706            _ => {}
1707        }
1708        i += 1;
1709    }
1710    None
1711}
1712
1713fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1714    let mut name = None;
1715    let mut colon_count = 0;
1716    let mut access_count = 0;
1717    for index in 0..node.child_count() {
1718        let child = node.child(index)?;
1719        match child.kind() {
1720            "identifier" | "type_identifier" if child.is_named() => {
1721                if name.is_some() {
1722                    return None;
1723                }
1724                let candidate = normalize_cpp_whitespace(node_text(child, source));
1725                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1726                    return None;
1727                }
1728                name = Some(candidate);
1729            }
1730            "template_function" | "template_type" if child.is_named() => {
1731                if name.is_some() {
1732                    return None;
1733                }
1734                let candidate = child
1735                    .child_by_field_name("name")
1736                    .and_then(|name| direct_identifier_name(name, source))?;
1737                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1738                    return None;
1739                }
1740                name = Some(candidate);
1741            }
1742            ":" if !child.is_named() => colon_count += 1,
1743            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1744            _ => return None,
1745        }
1746    }
1747    (colon_count == 1 && access_count == 1)
1748        .then_some(name)
1749        .flatten()
1750}
1751
1752fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1753    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1754        return false;
1755    }
1756    node.named_child(0)
1757        .and_then(|child| direct_identifier_name(child, source))
1758        .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1759}
1760
1761use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1762
1763fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1764    match node.kind() {
1765        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1766            recovered_base_atom(node, source)
1767        }
1768        // The macro-declaration grammar wraps an access keyword and the base
1769        // as a `macro_modifier_declarator`; the base is its declarator.
1770        "macro_modifier_declarator" => node
1771            .child_by_field_name("declarator")
1772            .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1773        "template_type" | "template_function" => node
1774            .child_by_field_name("name")
1775            .and_then(|name| recovered_malformed_base_name(name, source)),
1776        "ERROR" => None,
1777        "qualified_identifier" | "scoped_type_identifier" => {
1778            let suffix = node
1779                .child_by_field_name("name")
1780                .and_then(|name| recovered_malformed_base_name(name, source))?;
1781            let scope = node
1782                .child_by_field_name("scope")
1783                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1784            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1785                malformed_qualified_prefix(node, source)?
1786            } else {
1787                if malformed_qualified_prefix(node, source).is_some() {
1788                    return None;
1789                }
1790                scope
1791            };
1792            Some(format!("{prefix}::{suffix}"))
1793        }
1794        _ => None,
1795    }
1796}
1797
1798fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1799    let mut pending = vec![node];
1800    let mut components = Vec::new();
1801    while let Some(current) = pending.pop() {
1802        match current.kind() {
1803            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1804                components.push(recovered_base_atom(current, source)?);
1805            }
1806            "macro_modifier_declarator" => {
1807                pending.push(current.child_by_field_name("declarator")?);
1808            }
1809            "template_type" | "template_function" => {
1810                pending.push(current.child_by_field_name("name")?);
1811            }
1812            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1813                pending.push(current.child_by_field_name("name")?);
1814                if let Some(scope) = current.child_by_field_name("scope") {
1815                    // Malformed export bases can put the access keyword in
1816                    // `scope` and the real scope in the direct ERROR child.
1817                    // Keep the exact AST recovery used by the declaration.
1818                    if recovered_base_atom(scope, source).is_some_and(|name| {
1819                        matches!(name.as_str(), "public" | "protected" | "private")
1820                    }) {
1821                        pending.push(malformed_qualified_prefix_node(current, source)?);
1822                    } else {
1823                        if malformed_qualified_prefix_node(current, source).is_some() {
1824                            return None;
1825                        }
1826                        pending.push(scope);
1827                    }
1828                }
1829            }
1830            _ => return None,
1831        }
1832    }
1833    Some(components)
1834}
1835
1836fn recovered_base_specifier(
1837    node: Node<'_>,
1838    source: &str,
1839    is_virtual: bool,
1840) -> Option<CppBaseSpecifierFact> {
1841    let components = recovered_base_components(node, source)?;
1842    crate::source_facts::base_specifier_fact(
1843        node,
1844        components,
1845        is_virtual,
1846        CppAccessSpecifier::Unknown,
1847    )
1848}
1849
1850fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1851    // Recovery can put the qualifier in an ERROR sibling or inside the base
1852    // node. Read only the AST prefix through this base, resetting at preceding
1853    // inheritance delimiters so another base's qualifier cannot leak here.
1854    let mut pending = vec![container];
1855    let mut is_virtual = false;
1856    while let Some(node) = pending.pop() {
1857        if node.start_byte() >= base.end_byte() {
1858            break;
1859        }
1860        if node.child_count() != 0 {
1861            let mut cursor = node.walk();
1862            let children = node
1863                .children(&mut cursor)
1864                .take_while(|child| child.start_byte() < base.end_byte())
1865                .collect::<Vec<_>>();
1866            pending.extend(children.into_iter().rev());
1867            continue;
1868        }
1869        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1870            is_virtual = false;
1871        } else if node.kind() == "virtual"
1872            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1873        {
1874            is_virtual = true;
1875        }
1876    }
1877    is_virtual
1878}
1879
1880fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1881    if !matches!(
1882        node.kind(),
1883        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1884    ) {
1885        return None;
1886    }
1887    let name = normalize_cpp_whitespace(node_text(node, source));
1888    (!name.is_empty()).then_some(name)
1889}
1890
1891fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1892    malformed_qualified_prefix_node(node, source)
1893        .and_then(|prefix| recovered_base_atom(prefix, source))
1894}
1895
1896fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1897    let mut prefix = None;
1898    let mut cursor = node.walk();
1899    for error in node
1900        .named_children(&mut cursor)
1901        .filter(|child| child.kind() == "ERROR")
1902    {
1903        if prefix.is_some() {
1904            return None;
1905        }
1906        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1907        // the qualified name's scope and leaves `virtual` beside the real
1908        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1909        // component of the base's name (#2924).
1910        let mut error_cursor = error.walk();
1911        let atoms = error
1912            .named_children(&mut error_cursor)
1913            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1914            .collect::<Option<Vec<_>>>()?;
1915        let [atom] = atoms
1916            .iter()
1917            .filter(|(_, name)| name != "virtual")
1918            .collect::<Vec<_>>()[..]
1919        else {
1920            return None;
1921        };
1922        prefix = Some(atom.0);
1923    }
1924    prefix
1925}
1926
1927type RecoveredFunctionDefinitionClass<'tree> = (
1928    Node<'tree>,
1929    String,
1930    Option<Vec<String>>,
1931    Option<Vec<CppBaseSpecifierFact>>,
1932);
1933/// One declaration an attribute-like macro invocation swallowed into a
1934/// declaration-scope `ERROR`, with the byte range that spells it.
1935/// What [`stranded_declaration_run`] read out of one node.
1936struct StrandedRun<'tree> {
1937    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1938    /// Whether every part of the node read as part of a declaration. False when
1939    /// a part the reader does not understand ended it early, or when the last
1940    /// parts were types with no declarator after them. Callers that index what
1941    /// was found keep the declarations either way; a caller deciding whether a
1942    /// whole region is safe to index requires this.
1943    complete: bool,
1944}
1945
1946struct MacroWrappedDeclaration<'tree> {
1947    declarator: Node<'tree>,
1948    range: Range,
1949    /// Whether the recovered declaration spells `static`. The envelope hides
1950    /// that keyword from the ordinary linkage reader, which looks for it among
1951    /// a declaration node's own children, and internal linkage is what decides
1952    /// whether a header declaration and a body in another file are one symbol.
1953    is_static: bool,
1954}
1955
1956/// Whether `node` stands where declarations live: directly in the translation
1957/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1958/// stands in one of those.
1959///
1960/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1961/// where it stands. The case that matters is a whole translation unit the
1962/// parser could not close: whisper.cpp's `include/whisper.h` opens
1963/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1964/// so the braces never balance in the token stream tree-sitter sees, the file's
1965/// own root node is an `ERROR`, and every top-level declaration in the file is
1966/// a child of it (#3094). Without this the macro recoveries below read nothing
1967/// in such a file, because none of their envelopes is in a declaration scope
1968/// the parser managed to build.
1969fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1970    declaration_scope_container(node, ancestry).is_some()
1971}
1972
1973/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1974/// end is where a parse failure that starts at `node` can still be doing
1975/// damage: a declaration scope has no synchronization point of its own, so the
1976/// parser carries such a failure to the scope's close.
1977fn declaration_scope_container<'tree>(
1978    node: Node<'tree>,
1979    ancestry: &ParentIndex<'tree>,
1980) -> Option<Node<'tree>> {
1981    let mut parent = ancestry.parent(node)?;
1982    loop {
1983        match parent.kind() {
1984            "translation_unit" => return Some(parent),
1985            "declaration_list" => {
1986                return ancestry
1987                    .parent(parent)
1988                    .is_some_and(|grandparent| {
1989                        matches!(
1990                            grandparent.kind(),
1991                            "namespace_definition" | "linkage_specification"
1992                        )
1993                    })
1994                    .then_some(parent);
1995            }
1996            "ERROR" => match ancestry.parent(parent) {
1997                Some(grandparent) => parent = grandparent,
1998                // A root `ERROR` is the translation unit the parser could not
1999                // build, so its children stand at file scope.
2000                None => return Some(parent),
2001            },
2002            // A preprocessor conditional is a transparent declaration-scope
2003            // wrapper when it sits directly under the translation unit or a
2004            // namespace/linkage declaration list. Keep climbing through it;
2005            // the next ordinary construct still decides whether the enclosed
2006            // error is at declaration scope. In particular, a conditional
2007            // inside a function reaches its compound statement and is
2008            // rejected below rather than becoming a global recovery region.
2009            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2010                parent = ancestry.parent(parent)?;
2011            }
2012            _ => return None,
2013        }
2014    }
2015}
2016
2017/// Whether `node` is an `ERROR` the parser produced where declarations live.
2018fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2019    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2020}
2021
2022/// The identifier an object-like macro marker wraps. The grammar admits a lone
2023/// identifier as a `macro_modifier` in a declaration's specifier run, or as
2024/// the prefix of a `macro_modifier_declarator`, only where the declaration
2025/// would otherwise not parse, so the modifier is always the structured reading
2026/// of a macro the workspace never expands: it is provenance about the
2027/// declaration, never a declarator of its own.
2028pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2029    if node.kind() != "macro_modifier" {
2030        return None;
2031    }
2032    node.named_child(0)
2033        .filter(|child| child.kind() == "identifier")
2034}
2035
2036/// The modifier word and inner declarator a `macro_modifier_declarator`
2037/// wraps: the grammar's structured reading of an unexpanded object-like macro
2038/// before a declarator. In a statement-context class-body reparse the modifier
2039/// can also be the member's return type, which the grammar could not separate
2040/// from the declarator and admitted as a modifier instead.
2041pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2042    if node.kind() != "macro_modifier_declarator" {
2043        return None;
2044    }
2045    let mut cursor = node.walk();
2046    let modifier = node
2047        .named_children(&mut cursor)
2048        .find(|child| child.kind() == "macro_modifier")
2049        .and_then(macro_modifier_identifier)?;
2050    let declarator = node.child_by_field_name("declarator")?;
2051    Some((modifier, declarator))
2052}
2053
2054/// Whether `node` can only be part of what precedes a declarator -- a type, a
2055/// specifier, or a word of the attribute macro's own text that the lexer left
2056/// as a bare identifier or, under the macro-declaration grammar, wrapped as a
2057/// `macro_modifier` -- so a run of these followed by a declarator is one
2058/// declaration the parser failed to group.
2059fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2060    matches!(
2061        node.kind(),
2062        "identifier"
2063            | "type_identifier"
2064            | "primitive_type"
2065            | "sized_type_specifier"
2066            | "struct_specifier"
2067            | "union_specifier"
2068            | "enum_specifier"
2069            | "type_qualifier"
2070            | "storage_class_specifier"
2071            | "explicit_function_specifier"
2072            | "virtual_function_specifier"
2073            | "qualified_identifier"
2074            | "template_type"
2075            | "dependent_type"
2076            | "placeholder_type_specifier"
2077            | "macro_modifier"
2078            | "macro_type_specifier"
2079    )
2080}
2081
2082/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
2083/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
2084/// words the lexer reports as bare identifiers. Anything else in such an
2085/// `ERROR` stops the recovery, so a macro argument that carries structure this
2086/// recovery does not understand is never guessed at.
2087fn is_macro_argument_error(node: Node<'_>) -> bool {
2088    if node.kind() != "ERROR" {
2089        return false;
2090    }
2091    let mut cursor = node.walk();
2092    node.named_children(&mut cursor).all(|child| {
2093        matches!(
2094            child.kind(),
2095            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2096        )
2097    })
2098}
2099
2100/// The end of a recovered declaration: the declarator's end, extended across
2101/// the `;` the grammar left beside it inside the envelope.
2102///
2103/// The envelope's own end is the hard boundary. The parser hands the last
2104/// declaration's `;` to the sibling statement it recovered with, outside the
2105/// envelope, and a range that reached it would no longer lie inside one node --
2106/// which is how every reader (including the resolver's climb from a range to
2107/// the node that declares it) finds a recovered declaration again.
2108fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2109    declarator
2110        .next_sibling()
2111        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2112        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2113}
2114
2115/// The declarations `node` holds after an attribute-like macro cost the parser
2116/// their grouping, in source order.
2117///
2118/// The parser packs the parts into whichever slots it has left. In whisper's
2119/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
2120/// field takes the export macro, the real return type and the *next*
2121/// declaration's declarator both end up inside one sibling `ERROR`, and the
2122/// `declarator` field takes whatever declaration the recovery reached last. In
2123/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
2124/// arrive as bare identifiers and the attributed member and the member after it
2125/// share one declarator node.
2126///
2127/// Both are the same failure, so both get the same reading: flatten the node's
2128/// parts -- splicing each nested `ERROR`'s own children in place, since an
2129/// `ERROR` here is only a grouping failure -- and read the flat run as what it
2130/// spells, a run of type-and-specifier tokens followed by a declarator, over
2131/// and over.
2132///
2133/// Fails closed. A part that is neither a declarator nor something that can
2134/// only precede one ends the recovery there, and the declarations before it are
2135/// kept.
2136fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2137    let mut parts = Vec::new();
2138    let mut cursor = node.walk();
2139    for child in node.named_children(&mut cursor) {
2140        if child.kind() == "ERROR" {
2141            let mut error_cursor = child.walk();
2142            parts.extend(child.named_children(&mut error_cursor));
2143        } else {
2144            parts.push(child);
2145        }
2146    }
2147
2148    let mut declarations = Vec::new();
2149    let mut start = None;
2150    let mut is_static = false;
2151    let mut complete = true;
2152    for part in parts {
2153        if part.kind() == "comment" {
2154            continue;
2155        }
2156        if let Some(declarator) = extract_function_declarator(part) {
2157            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2158            declarations.push(MacroWrappedDeclaration {
2159                declarator,
2160                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2161                is_static,
2162            });
2163            is_static = false;
2164            continue;
2165        }
2166        if !is_recovered_declaration_type_part(part) {
2167            complete = false;
2168            break;
2169        }
2170        is_static |= part.kind() == "storage_class_specifier"
2171            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2172        start.get_or_insert(part.start_byte());
2173    }
2174    StrandedRun {
2175        declarations,
2176        complete: complete && start.is_none(),
2177    }
2178}
2179
2180/// The declarations an attribute-like macro invocation swallowed into a
2181/// declaration-scope `ERROR`, in source order.
2182///
2183/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2184/// declaration in a macro call:
2185///
2186/// ```text
2187/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2188///                  struct llama_model * model,
2189///           struct llama_context_params   params),
2190///         "use llama_init_from_model instead");
2191/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2192/// ```
2193///
2194/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2195/// holding the macro name, the wrapped declaration as a
2196/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2197/// following declaration as a further `parameter_declaration` until it
2198/// recovers. That is what removed `llama_tokenize` from the index and left the
2199/// seven-argument call in `talk-llama.cpp` with only that file's own
2200/// three-parameter `static` overload to choose from (#2552, and the
2201/// `LLAMA_API` half of #2551).
2202///
2203/// Every part of every swallowed declaration is still a real node; only their
2204/// grouping is lost. This rebuilds the grouping and reads the nodes.
2205///
2206/// Under the macro-declaration grammar the same invocation keeps the same
2207/// `ERROR` envelope, but the parts inside it are structured further: the macro
2208/// head is a whole `function_declarator` whose parameter list holds the
2209/// arguments, an export-macro word is a `macro_modifier`, a following
2210/// declaration is a `macro_modifier_declarator` or a bare
2211/// `function_declarator` in a sibling `ERROR`, and the hint string explodes
2212/// into word nodes between the `ERROR` nodes that hold its quotes. The reading
2213/// is the same either way: the invocation's own arguments, then one
2214/// declaration at a time until something that is not a declaration stops it.
2215fn macro_wrapped_declarations<'tree>(
2216    envelope: Node<'tree>,
2217    source: &str,
2218    ancestry: &ParentIndex<'tree>,
2219) -> Vec<MacroWrappedDeclaration<'tree>> {
2220    let mut declarations = Vec::new();
2221    if !is_declaration_scope_error(envelope, ancestry) {
2222        return declarations;
2223    }
2224    let mut cursor = envelope.walk();
2225    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2226    let [head, rest @ ..] = children.as_slice() else {
2227        return declarations;
2228    };
2229    let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2230        rest.to_vec()
2231    } else if head.kind() == "function_declarator"
2232        && head
2233            .child_by_field_name("declarator")
2234            .is_some_and(|name| name.kind() == "identifier")
2235    {
2236        let Some(parameters) = head
2237            .child_by_field_name("parameters")
2238            .filter(|parameters| parameters.kind() == "parameter_list")
2239        else {
2240            return declarations;
2241        };
2242        let mut parameters_cursor = parameters.walk();
2243        parameters.named_children(&mut parameters_cursor).collect()
2244    } else {
2245        return declarations;
2246    };
2247    let mut wrapped_declaration_seen = false;
2248    // The hint string's exploded words sit between the `ERROR` nodes holding
2249    // its quotes; inside that bracket they are string content, not
2250    // declaration parts, and they must not start a declaration's range.
2251    let mut in_hint = false;
2252    // Where the specifier words gathered for the declaration whose declarator
2253    // has not arrived yet. Only the macro-declaration grammar's envelopes use
2254    // this: in them an export-macro word is a sibling of the declarator
2255    // rather than a part of its `parameter_declaration`.
2256    let mut pending_start = None;
2257    let mut is_static = false;
2258    for argument in arguments {
2259        if argument.kind() == "comment" {
2260            continue;
2261        }
2262        if argument.kind() == "ERROR"
2263            && (0..argument.child_count())
2264                .filter(|index| {
2265                    argument
2266                        .child(*index)
2267                        .is_some_and(|child| child.kind() == "\"")
2268                })
2269                .count()
2270                % 2
2271                == 1
2272        {
2273            // One of the quote `ERROR`s bracketing the hint string.
2274            in_hint = !in_hint;
2275            pending_start = None;
2276            continue;
2277        }
2278        if in_hint {
2279            continue;
2280        }
2281        match argument.kind() {
2282            "parameter_declaration" => {
2283                let recovered = stranded_declaration_run(argument, source).declarations;
2284                if recovered.is_empty() {
2285                    break;
2286                }
2287                wrapped_declaration_seen = true;
2288                declarations.extend(recovered);
2289            }
2290            // A specifier word of the declaration whose declarator the parser
2291            // handed to a later sibling.
2292            "macro_modifier" if wrapped_declaration_seen => {
2293                pending_start.get_or_insert(argument.start_byte());
2294            }
2295            "storage_class_specifier" if wrapped_declaration_seen => {
2296                pending_start.get_or_insert(argument.start_byte());
2297                is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2298            }
2299            "macro_modifier_declarator" if wrapped_declaration_seen => {
2300                let Some(declarator) = extract_function_declarator(argument) else {
2301                    break;
2302                };
2303                let start = pending_start
2304                    .take()
2305                    .unwrap_or_else(|| argument.start_byte());
2306                declarations.push(MacroWrappedDeclaration {
2307                    declarator,
2308                    range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2309                    is_static,
2310                });
2311                is_static = false;
2312            }
2313            // The hint string, and only that: an argument the recovery cannot
2314            // read as a declaration is admitted before the wrapped declaration
2315            // is found, so a macro whose first argument is not a declaration
2316            // recovers nothing.
2317            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2318            // A declaration the parser shredded to a bare function declarator
2319            // and its terminator, its specifier words left as the siblings
2320            // before it.
2321            "ERROR" if wrapped_declaration_seen => {
2322                let mut error_cursor = argument.walk();
2323                let mut parts = argument.named_children(&mut error_cursor);
2324                let Some(declarator) = parts
2325                    .next()
2326                    .filter(|part| part.kind() == "function_declarator")
2327                    .filter(|_| parts.next().is_none())
2328                else {
2329                    break;
2330                };
2331                let start = pending_start
2332                    .take()
2333                    .unwrap_or_else(|| declarator.start_byte());
2334                declarations.push(MacroWrappedDeclaration {
2335                    declarator,
2336                    range: cpp_recovery_window(
2337                        source,
2338                        start,
2339                        recovered_declaration_end(declarator),
2340                    ),
2341                    is_static,
2342                });
2343                is_static = false;
2344            }
2345            _ => break,
2346        }
2347    }
2348    declarations
2349}
2350
2351/// What one macro invocation swallowed when it collapsed a whole run of
2352/// declarations.
2353struct CollapsedMacroDeclarationRun {
2354    /// The byte just past the `;` that closes the invocation, which is where
2355    /// the declarations it swallowed begin.
2356    invocation_end: usize,
2357    /// The byte this recovery owns to: the close of the enclosing declaration
2358    /// scope when the invocation swallowed the declarations written after it,
2359    /// and the invocation's own end when the parser instead spread the
2360    /// invocation itself across the container's children and swallowed nothing.
2361    region_end: usize,
2362}
2363
2364/// The tokens one collapsed macro invocation spans, in source order: the leaves
2365/// of `node`, then the leaves of the siblings written after it. Comments and
2366/// the parser's own MISSING tokens are skipped; neither is in the source the
2367/// invocation is written in.
2368///
2369/// The parser puts the parts of a failed invocation wherever it has room. It
2370/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2371/// `expression_statement` it recovered with; it spreads them flat over the
2372/// container's children; it packs the tail of the file into one of the
2373/// arguments. The grouping therefore says nothing, but the token order still
2374/// spells the invocation, which is what this reads.
2375struct MacroInvocationTokens<'tree> {
2376    stack: Vec<Node<'tree>>,
2377    /// The last sibling handed to `stack`, and the point the next one is asked
2378    /// for. Held as the node rather than as its already-computed successor so
2379    /// the successor is asked for only when the walk actually runs off the end
2380    /// of what it has: `Node::next_sibling` recovers the parent first, which
2381    /// re-descends from the root, so it costs the node's position in the tree.
2382    /// Nearly every node this iterator is built for is rejected on its first
2383    /// token, before any sibling is needed, and computing one eagerly made that
2384    /// rejection cost a walk of the whole container (capstone's generated
2385    /// instruction tables, t19).
2386    frontier: Option<Node<'tree>>,
2387}
2388
2389impl<'tree> MacroInvocationTokens<'tree> {
2390    fn new(node: Node<'tree>) -> Self {
2391        Self {
2392            stack: vec![node],
2393            frontier: Some(node),
2394        }
2395    }
2396}
2397
2398impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2399    type Item = Node<'tree>;
2400
2401    fn next(&mut self) -> Option<Node<'tree>> {
2402        loop {
2403            let Some(node) = self.stack.pop() else {
2404                let sibling = self.frontier?.next_sibling()?;
2405                self.frontier = Some(sibling);
2406                self.stack.push(sibling);
2407                continue;
2408            };
2409            if node.child_count() == 0 {
2410                if node.kind() == "comment" || node.is_missing() {
2411                    continue;
2412                }
2413                return Some(node);
2414            }
2415            let mut cursor = node.walk();
2416            let children = node.children(&mut cursor).collect::<Vec<_>>();
2417            self.stack.extend(children.into_iter().rev());
2418        }
2419    }
2420}
2421
2422/// The macro invocation that begins at `node` when the parser could not group
2423/// it, or `None` when `node` does not begin one.
2424///
2425/// whisper.cpp's bundled `llama.h` writes
2426///
2427/// ```text
2428/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2429///                          const char * path_model,
2430///           struct llama_model_params   params),
2431///         "use llama_model_load_from_file instead");
2432/// ```
2433///
2434/// and its own `include/whisper.h` writes
2435///
2436/// ```text
2437/// WHISPER_DEPRECATED(
2438///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2439///     "use whisper_init_from_file_with_params instead"
2440/// );
2441/// ```
2442///
2443/// tree-sitter cannot know either name is a macro, so it reads the name and its
2444/// `(` as a function declarator whose close it never finds, and carries that
2445/// failure forward over the declarations written after it. In `llama.h` that
2446/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2447/// (#2551). In `whisper.h` it instead flattens the invocations over the
2448/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2449/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2450/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2451/// `function_definition` when a brace block falls in a swallowed tail, because
2452/// the parser borrows it for the body the bogus definition needs, and an
2453/// `ERROR` or a bare token when none does. None of that says anything about the
2454/// construct, so the node's kind is not the criterion; the token order is.
2455///
2456/// The invocation ends at the `)` that closes its own `(`, immediately followed
2457/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2458/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2459/// its own, so a `;` reached before the close means this is some other
2460/// construct -- a macro-defined function body, say -- and the ordinary readers
2461/// keep the node.
2462///
2463/// Fails closed everywhere else too. The head must be an export-macro-shaped
2464/// token followed by `(`, the node must stand where declarations live, and an
2465/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2466/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2467/// nothing past it -- is left to that reader.
2468fn collapsed_macro_declaration_run<'tree>(
2469    node: Node<'tree>,
2470    source: &str,
2471    ancestry: &ParentIndex<'tree>,
2472) -> Option<CollapsedMacroDeclarationRun> {
2473    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2474    let mut tokens = MacroInvocationTokens::new(node);
2475    let name = tokens.next()?;
2476    if !matches!(name.kind(), "identifier" | "type_identifier")
2477        || !cpp_export_macro_token(node_text(name, source))
2478    {
2479        return None;
2480    }
2481    if tokens.next()?.kind() != "(" {
2482        return None;
2483    }
2484    let mut depth = 1usize;
2485    let invocation_end = loop {
2486        let token = tokens.next()?;
2487        match token.kind() {
2488            "(" => depth += 1,
2489            ";" => return None,
2490            ")" => {
2491                depth -= 1;
2492                if depth == 0 {
2493                    let semicolon = tokens.next()?;
2494                    if semicolon.kind() != ";" {
2495                        return None;
2496                    }
2497                    break semicolon.end_byte();
2498                }
2499            }
2500            _ => {}
2501        }
2502    };
2503    // An invocation that reaches the end of its own declaration scope swallowed
2504    // nothing: there is nothing after it left to recover, and the ordinary
2505    // readers have the invocation itself. This is also what stops the scan
2506    // below from re-entering itself, since the region it reparses for one
2507    // invocation ends exactly at that invocation's `;`.
2508    if invocation_end >= container_end {
2509        return None;
2510    }
2511    // Where the damage ends. A node that reaches past its own invocation packed
2512    // real declarations into the invocation's argument list, so the parser was
2513    // still failing when it got there and everything to the close of the scope
2514    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2515    // line 455 with it, and every declaration from there to the end of the file
2516    // is shredded. A node that stops at or before the invocation's `;` holds
2517    // nothing but the invocation, whose own parts the parser spread over the
2518    // siblings after it.
2519    let region_end = if node.end_byte() > invocation_end {
2520        container_end
2521    } else {
2522        invocation_end
2523    };
2524    Some(CollapsedMacroDeclarationRun {
2525        invocation_end,
2526        region_end,
2527    })
2528}
2529
2530/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2531/// ordinary class body, and the members it swallowed.
2532///
2533/// Botan deprecates members that way:
2534///
2535/// ```text
2536/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2537/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2538/// ```
2539///
2540/// The parser reads the macro name as the member's type and its argument list
2541/// as a parenthesized declarator, which then swallows the attributed member
2542/// *and* the member written after it: a `field_declaration` whose `type` is a
2543/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2544/// opening with an `ERROR` whose first part is a bare identifier -- the first
2545/// word of the string, which the lexer could not keep together.
2546///
2547/// The macro's spelling is not the criterion; that structural shape is. The
2548/// returned declarations are in source order, the first being the attributed
2549/// member and the rest the members the declarator swallowed after it.
2550fn string_attribute_macro_member_declarators<'tree>(
2551    field: Node<'tree>,
2552    source: &str,
2553) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2554    if field.kind() != "field_declaration"
2555        || field
2556            .child_by_field_name("type")
2557            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2558    {
2559        return None;
2560    }
2561    let declarator = field.child_by_field_name("declarator")?;
2562    if declarator.kind() != "parenthesized_declarator" {
2563        return None;
2564    }
2565    let opening = declarator.named_child(0)?;
2566    if opening.kind() != "ERROR"
2567        || opening
2568            .named_child(0)
2569            .is_none_or(|word| word.kind() != "identifier")
2570    {
2571        return None;
2572    }
2573    let declarations = stranded_declaration_run(declarator, source).declarations;
2574    (!declarations.is_empty()).then_some(declarations)
2575}
2576
2577/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2578/// export-macro class body leaves in front of the members that macro decorated.
2579///
2580/// The reparse reads the class body as statements, so the attribute becomes a
2581/// call statement of its own -- with the `;` the grammar had to invent -- and
2582/// the members after it are stranded in the `ERROR` that follows.
2583fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2584    let Some(call) = (node.kind() == "expression_statement")
2585        .then(|| node.named_child(0))
2586        .flatten()
2587        .filter(|child| child.kind() == "call_expression")
2588    else {
2589        return false;
2590    };
2591    call.child_by_field_name("function")
2592        .is_some_and(|function| function.kind() == "identifier")
2593        && call
2594            .child_by_field_name("arguments")
2595            .is_some_and(|arguments| {
2596                let mut cursor = arguments.walk();
2597                arguments.named_child_count() > 0
2598                    && arguments
2599                        .named_children(&mut cursor)
2600                        .all(|argument| argument.kind() == "string_literal")
2601            })
2602}
2603
2604/// The start byte of the call the region reparse left where an access-labeled
2605/// constructor was written.
2606///
2607/// A constructor is a member only inside a class body. The export-macro class
2608/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2609/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2610/// comma-expression under `private:`, with no declarator left in the tree. It
2611/// is still spelled in the source, though, starting at the one call under the
2612/// label whose callee is the class's own name. Botan's private six-parameter
2613/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2614/// call is an ambiguity this declines, and so is a reparse from that byte that
2615/// does not yield exactly one constructor declarator there.
2616fn cpp_access_label_constructor_call_start(
2617    node: Node<'_>,
2618    class_name: &str,
2619    source: &str,
2620) -> Option<usize> {
2621    if node.kind() != "labeled_statement" {
2622        return None;
2623    }
2624    let label = node.named_child(0)?;
2625    if label.kind() != "statement_identifier"
2626        || !matches!(
2627            node_text(label, source).trim(),
2628            "public" | "private" | "protected"
2629        )
2630    {
2631        return None;
2632    }
2633    let mut starts = Vec::new();
2634    let mut stack = vec![node];
2635    while let Some(current) = stack.pop() {
2636        if current.kind() == "call_expression"
2637            && current
2638                .child_by_field_name("function")
2639                .is_some_and(|function| {
2640                    function.kind() == "identifier"
2641                        && node_text(function, source).trim() == class_name
2642                })
2643        {
2644            starts.push(current.start_byte());
2645        }
2646        // The macro-declaration grammar reads the constructor head as a
2647        // declaration whose type is the class name and whose declarators are
2648        // the shredded parameter list and initializers; the head's start is
2649        // the type's start.
2650        if current.kind() == "declaration"
2651            && current
2652                .child_by_field_name("type")
2653                .is_some_and(|type_node| {
2654                    type_node.kind() == "type_identifier"
2655                        && node_text(type_node, source).trim() == class_name
2656                })
2657        {
2658            starts.push(
2659                current
2660                    .child_by_field_name("type")
2661                    .expect("checked declaration type")
2662                    .start_byte(),
2663            );
2664        }
2665        let mut cursor = current.walk();
2666        stack.extend(current.named_children(&mut cursor));
2667    }
2668    let [start] = starts.as_slice() else {
2669        return None;
2670    };
2671    Some(*start)
2672}
2673
2674/// The `function_definition` that gives `declarator` a body, following only the
2675/// declarator chain, so a recovered callable knows whether it is a declaration
2676/// or a definition without anyone having to say.
2677fn cpp_declarator_function_definition<'tree>(
2678    declarator: Node<'tree>,
2679    ancestry: &ParentIndex<'tree>,
2680) -> Option<Node<'tree>> {
2681    let mut current = declarator;
2682    while let Some(parent) = ancestry.parent(current) {
2683        match parent.kind() {
2684            "function_definition" if parent.child_by_field_name("body").is_some() => {
2685                return Some(parent);
2686            }
2687            "pointer_declarator"
2688            | "reference_declarator"
2689            | "parenthesized_declarator"
2690            | "array_declarator" => current = parent,
2691            _ => return None,
2692        }
2693    }
2694    None
2695}
2696
2697/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2698/// declaration walk still reaches, so a recovery that runs ahead of that walk
2699/// would name what it finds without the namespace.
2700fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2701    let mut current = node;
2702    while let Some(parent) = ancestry.parent(current) {
2703        if parent.kind() == "namespace_definition"
2704            && parent.child_by_field_name("body") == Some(current)
2705        {
2706            return true;
2707        }
2708        current = parent;
2709    }
2710    false
2711}
2712
2713/// Whether the source a recovered callable's byte range spells is a definition
2714/// (a declarator with a body) rather than a declaration.
2715///
2716/// A callable recovered from a mangled region owns no node of its own in the
2717/// file's tree, so the occurrence-role scan that climbs from the range to the
2718/// node containing it lands on whatever container the parser left and answers
2719/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2720/// constructor a declaration and left its call site with nothing to navigate to
2721/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2722/// extraction used, and read the answer from the one declaration it spells.
2723///
2724/// `None` when the range is not one recovered declaration on its own, which is
2725/// the case for every ordinary declaration, so callers keep their own answer.
2726pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2727    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2728    let root = tree.root_node();
2729    let mut cursor = root.walk();
2730    let items = root
2731        .named_children(&mut cursor)
2732        .filter(|child| child.kind() != "comment")
2733        .collect::<Vec<_>>();
2734    let [item] = items.as_slice() else {
2735        return None;
2736    };
2737    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2738        return None;
2739    }
2740    match item.kind() {
2741        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2742        "declaration" | "field_declaration" => Some(false),
2743        _ => None,
2744    }
2745}
2746
2747/// Whether `node` is an envelope an attribute-like macro invocation left where
2748/// declarations were written: the declaration-scope `ERROR`
2749/// [`macro_wrapped_declarations`] reads, or the collapsed run
2750/// [`collapsed_macro_declaration_run`] reads.
2751///
2752/// Extraction and resolution must read one definition of this shape. The
2753/// resolver climbs from a declaration's recorded byte range to the node that
2754/// declares it, and a recovered declaration's range lies inside one of these
2755/// envelopes rather than inside a `declaration` node, so the climb stops here
2756/// (the same role `is_recovered_exported_class_container` plays for a recovered
2757/// class).
2758pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2759    let ancestry = ParentIndex::unindexed();
2760    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2761        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2762}
2763
2764fn recover_exported_class_function_definition<'tree>(
2765    node: Node<'tree>,
2766    source: &str,
2767) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2768    if node.kind() != "function_definition" {
2769        return None;
2770    }
2771    if let Some(prefix) = node.prev_named_sibling()
2772        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2773        && recovered.range.end_byte == node.end_byte()
2774    {
2775        return Some((
2776            node,
2777            recovered.name,
2778            recovered.raw_supertypes,
2779            recovered.base_facts,
2780        ));
2781    }
2782    let type_node = node.child_by_field_name("type")?;
2783    let declarator = node.child_by_field_name("declarator")?;
2784
2785    if matches!(
2786        type_node.kind(),
2787        "class_specifier" | "struct_specifier" | "union_specifier"
2788    ) {
2789        let type_name = type_node
2790            .child_by_field_name("name")
2791            .and_then(|name| direct_identifier_name(name, source));
2792        let exported_macro_type = type_name
2793            .as_ref()
2794            .is_some_and(|name| cpp_export_macro_token(name));
2795        if exported_macro_type {
2796            let mut cursor = node.walk();
2797            let errors_before_declarator = node
2798                .named_children(&mut cursor)
2799                .filter(|child| {
2800                    child.kind() == "ERROR"
2801                        && child.start_byte() >= type_node.end_byte()
2802                        && child.end_byte() <= declarator.start_byte()
2803                })
2804                .collect::<Vec<_>>();
2805            if let Some(name) = errors_before_declarator
2806                .iter()
2807                .find_map(|error| displaced_exported_class_name(*error, source))
2808            {
2809                let base_node = errors_before_declarator
2810                    .iter()
2811                    .any(|error| malformed_inheritance_syntax(*error))
2812                    .then_some(declarator);
2813                let raw_supertypes = base_node
2814                    .and_then(|base| recovered_malformed_base_name(base, source))
2815                    .map(|base| vec![base]);
2816                let base_facts = base_node
2817                    .and_then(|base| {
2818                        recovered_base_specifier(
2819                            base,
2820                            source,
2821                            recovered_base_is_virtual(node, base, source),
2822                        )
2823                    })
2824                    .map(|base| vec![base]);
2825                return Some((node, name, raw_supertypes, base_facts));
2826            }
2827            if errors_before_declarator
2828                .iter()
2829                .any(|error| malformed_inheritance_syntax(*error))
2830            {
2831                return None;
2832            }
2833        }
2834        if !exported_macro_type
2835            && let Some(name) = type_name
2836            && !cpp_export_macro_token(&name)
2837            && let Some(base) =
2838                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2839        {
2840            let base_node =
2841                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2842            let base_facts = Some(vec![recovered_base_specifier(
2843                base_node,
2844                source,
2845                recovered_base_is_virtual(node, base_node, source),
2846            )?]);
2847            return Some((node, name, Some(vec![base]), base_facts));
2848        }
2849        // The macro-declaration grammar reads the displaced class name and the
2850        // `final` that follows it as a `macro_modifier_declarator`; read the
2851        // name positionally from the head's tokens, the same rule the other
2852        // recovered export heads use (#2557).
2853        let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2854            (declarator.kind() == "macro_modifier_declarator")
2855                .then(|| node.child_by_field_name("body"))
2856                .flatten()
2857                .filter(|body| body.kind() == "compound_statement")
2858                .and_then(|body| recovered_export_head_name(node, body, source))
2859                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2860        });
2861        if let Some(name) = declarator_name
2862            && exported_macro_type
2863            && !cpp_export_macro_token(&name)
2864        {
2865            let base_node = exported_macro_type
2866                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2867                .flatten();
2868            let raw_supertypes = base_node
2869                .and_then(|base| recovered_malformed_base_name(base, source))
2870                .map(|base| vec![base]);
2871            let base_facts = base_node
2872                .and_then(|base| {
2873                    recovered_base_specifier(
2874                        base,
2875                        source,
2876                        recovered_base_is_virtual(node, base, source),
2877                    )
2878                })
2879                .map(|base| vec![base]);
2880            return Some((node, name, raw_supertypes, base_facts));
2881        }
2882        if declarator.kind() == "parenthesized_declarator"
2883            && type_node
2884                .child_by_field_name("name")
2885                .and_then(|name| direct_identifier_name(name, source))
2886                .is_some_and(|name| cpp_export_macro_token(&name))
2887        {
2888            if let Some((name, base_node)) =
2889                recovered_function_like_export_class_owner_node(declarator, source)
2890            {
2891                return Some((
2892                    node,
2893                    name,
2894                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2895                    Some(vec![recovered_base_specifier(
2896                        base_node,
2897                        source,
2898                        recovered_base_is_virtual(node, base_node, source),
2899                    )?]),
2900                ));
2901            }
2902            let body_start = node
2903                .child_by_field_name("body")
2904                .map(|body| body.start_byte())
2905                .unwrap_or(node.end_byte());
2906            let mut cursor = node.walk();
2907            if let Some(name) = node
2908                .named_children(&mut cursor)
2909                .filter(|child| {
2910                    child.kind() == "ERROR"
2911                        && child.start_byte() >= declarator.end_byte()
2912                        && child.end_byte() <= body_start
2913                })
2914                .find_map(|error| declarator_name_from_node(error, source))
2915            {
2916                return Some((node, name, None, None));
2917            }
2918            // The macro-declaration grammar keeps a head without a base clause
2919            // whole inside the declarator: `( ERROR(2, 2)) TOTP final` with a
2920            // missing `)`, the name and `final` wrapped as a
2921            // `macro_modifier_declarator` or the name left bare. Read the name
2922            // positionally from the declarator's own tokens (#2557). The macro
2923            // is the `type` field, so a capitalized name (`TOTP`) is the class.
2924            if let Some(body) = node
2925                .child_by_field_name("body")
2926                .filter(|body| body.kind() == "compound_statement")
2927                && let Some(name_node) =
2928                    export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2929            {
2930                let name = normalize_cpp_whitespace(node_text(name_node, source));
2931                if !name.is_empty() {
2932                    let (bases, base_facts) = recovered_export_head_base_data(
2933                        declarator,
2934                        name_node.end_byte(),
2935                        body.start_byte(),
2936                        source,
2937                    );
2938                    return Some((
2939                        node,
2940                        name,
2941                        (!bases.is_empty()).then_some(bases),
2942                        (!base_facts.is_empty()).then_some(base_facts),
2943                    ));
2944                }
2945            }
2946        }
2947    }
2948
2949    // The macro-declaration grammar reads a namespace-opening macro before an
2950    // export-macro class head (`NS_ENTER(ns) class API Service { ... }`) with
2951    // the invocation as the `macro_type_specifier` type and the class head as
2952    // a `macro_modifier_declarator` chain. The class name is the last head
2953    // token before the body, per the positional rule (#2557).
2954    if type_node.kind() == "macro_type_specifier"
2955        && declarator.kind() == "macro_modifier_declarator"
2956        && type_node
2957            .child_by_field_name("name")
2958            .and_then(|name| direct_identifier_name(name, source))
2959            .is_some_and(|name| cpp_export_macro_token(&name))
2960    {
2961        let body = node
2962            .child_by_field_name("body")
2963            .filter(|body| body.kind() == "compound_statement")?;
2964        let tokens = export_head_tokens(node, Some(body));
2965        let has_class_keyword = tokens.iter().any(|token| {
2966            matches!(token.kind(), "identifier" | "type_identifier")
2967                && matches!(node_text(*token, source), "class" | "struct" | "union")
2968        });
2969        if !has_class_keyword {
2970            return None;
2971        }
2972        let name_node = recovered_export_head_name(node, body, source)?;
2973        let name = normalize_cpp_whitespace(node_text(name_node, source));
2974        if name.is_empty() || cpp_export_macro_token(&name) {
2975            return None;
2976        }
2977        return Some((node, name, None, None));
2978    }
2979
2980    let declarator_text = direct_identifier_name(declarator, source)?;
2981    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2982        return None;
2983    }
2984    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
2985}
2986
2987fn recovered_function_like_export_class_owner_node<'tree>(
2988    declarator: Node<'tree>,
2989    source: &str,
2990) -> Option<(String, Node<'tree>)> {
2991    if declarator.kind() != "parenthesized_declarator" {
2992        return None;
2993    }
2994    let mut cursor = declarator.walk();
2995    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2996    let [prefix, base] = children.as_slice() else {
2997        return None;
2998    };
2999    if prefix.kind() != "ERROR"
3000        || !matches!(
3001            base.kind(),
3002            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3003        )
3004    {
3005        return None;
3006    }
3007    let mut identifiers = Vec::new();
3008    let mut prefix_cursor = prefix.walk();
3009    let mut parts = prefix
3010        .named_children(&mut prefix_cursor)
3011        .collect::<Vec<_>>();
3012    // The macro-declaration grammar wraps the displaced name and its `final`
3013    // as a `macro_modifier_declarator`; read the identifiers it wraps the same
3014    // way as the bare ones the older grammar left here.
3015    while let Some(child) = parts.pop() {
3016        match child.kind() {
3017            "number_literal" | "string_literal" | "char_literal" => {}
3018            "identifier" | "type_identifier" => {
3019                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3020            }
3021            "macro_modifier" => {
3022                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3023            }
3024            "macro_modifier_declarator" => {
3025                parts.extend(child.named_children(&mut child.walk()));
3026            }
3027            _ => return None,
3028        }
3029    }
3030    identifiers.reverse();
3031    let name = match identifiers.as_slice() {
3032        [name] => name.clone(),
3033        [name, final_token] if final_token == "final" => name.clone(),
3034        _ => return None,
3035    };
3036    if name.is_empty() || cpp_export_macro_token(&name) {
3037        return None;
3038    }
3039    Some((name, *base))
3040}
3041
3042/// Collect the base names and source facts tree-sitter scattered across the
3043/// recovered head of a function-like export-macro class. A base arrives either
3044/// as a direct sibling identifier or inside the `ERROR` node the grammar
3045/// produced for a `: public Base` fragment. The grammar leaves the `final`
3046/// specifier and access specifiers in the same position as the bases, so drop
3047/// them.
3048/// The bases a recovered function-like export-macro class head names between
3049/// `after` (the end of the class name, or of the access specifier that stands
3050/// in for it) and `before` (the start of the body, or of the `init_declarator`
3051/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
3052/// fragments, and one fragment can also hold the class name and `final`
3053/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
3054/// than by the fragment that holds it.
3055fn recovered_export_head_base_data(
3056    node: Node<'_>,
3057    after: usize,
3058    before: usize,
3059    source: &str,
3060) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3061    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3062    let mut base_nodes = Vec::new();
3063    let mut cursor = node.walk();
3064    for child in node.named_children(&mut cursor) {
3065        if child.kind() == "ERROR" {
3066            let mut error_cursor = child.walk();
3067            base_nodes.extend(
3068                child
3069                    .named_children(&mut error_cursor)
3070                    .filter(within)
3071                    .filter_map(|part| {
3072                        recovered_malformed_base_name(part, source).map(|_| (part, child))
3073                    }),
3074            );
3075        } else if child.kind() == "macro_modifier_declarator" {
3076            // The modifier words are access specifiers or decoration; the base
3077            // is the wrapper's terminal declarator.
3078            let mut current = child;
3079            while let Some(inner) = current.child_by_field_name("declarator") {
3080                if inner.kind() == "macro_modifier_declarator" {
3081                    current = inner;
3082                    continue;
3083                }
3084                if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3085                    base_nodes.push((inner, child));
3086                }
3087                break;
3088            }
3089        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3090            base_nodes.push((child, node));
3091        }
3092    }
3093    let mut bases = Vec::new();
3094    let mut facts = Vec::new();
3095    for (base, container) in base_nodes {
3096        let Some(name) = recovered_malformed_base_name(base, source) else {
3097            continue;
3098        };
3099        if matches!(
3100            name.as_str(),
3101            "final" | "public" | "protected" | "private" | "virtual"
3102        ) {
3103            continue;
3104        }
3105        bases.push(name);
3106        if let Some(fact) = recovered_base_specifier(
3107            base,
3108            source,
3109            recovered_base_is_virtual(container, base, source),
3110        ) {
3111            facts.push(fact);
3112        }
3113    }
3114    (bases, facts)
3115}
3116
3117fn recovered_export_head_bases(
3118    node: Node<'_>,
3119    after: usize,
3120    before: usize,
3121    source: &str,
3122) -> Vec<String> {
3123    recovered_export_head_base_data(node, after, before, source).0
3124}
3125
3126/// Whether `token` is the `final` that closes a recovered class head. The
3127/// grammar keeps it as the `final` keyword when it recognised the head
3128/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
3129/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
3130fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3131    if token.is_named() {
3132        token.kind() == "identifier" && node_text(token, source) == "final"
3133    } else {
3134        token.kind() == "final"
3135    }
3136}
3137
3138/// The class name of a recovered function-like export-macro class head, read by
3139/// position rather than spelling (#2557). `node` is the sibling tree-sitter
3140/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
3141/// carries the body. The head's tokens, in source order with `ERROR` fragments
3142/// flattened, read `macros... name [final] [: bases...]`: the name is the last
3143/// identifier before the head ends, and the head ends at `final`, at the base
3144/// clause `:`, or at `tail`. Every identifier before the name is decoration
3145/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
3146/// (`X509_CA`) is a class name like any other.
3147///
3148/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
3149/// rule, not a token of the source, so it is never the name.
3150fn recovered_export_head_name<'tree>(
3151    node: Node<'tree>,
3152    tail: Node<'tree>,
3153    source: &str,
3154) -> Option<Node<'tree>> {
3155    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3156}
3157
3158/// The class name of a head whose tokens straddle the recovered pair: the
3159/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
3160/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
3161/// The positional rule is the same, read over the two token runs in source
3162/// order, so a name in either node wins by position rather than by which node
3163/// holds it.
3164fn recovered_export_pair_head_name<'tree>(
3165    prefix: Node<'tree>,
3166    sibling: Node<'tree>,
3167    tail: Node<'tree>,
3168    source: &str,
3169) -> Option<Node<'tree>> {
3170    let mut tokens = export_head_tokens(prefix, None);
3171    tokens.extend(export_head_tokens(sibling, Some(tail)));
3172    export_head_name_from_tokens(&tokens, source)
3173}
3174
3175/// One node's head tokens in source order, with `ERROR` fragments spliced in
3176/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
3177/// before `tail` when the caller names one. A `macro_modifier` or
3178/// `macro_modifier_declarator` is the macro-declaration grammar's structured
3179/// form of the same grouping failure, so its children join the run in place
3180/// the same way, recursively.
3181fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3182    let mut tokens = Vec::new();
3183    let mut stack = Vec::new();
3184    let mut cursor = node.walk();
3185    stack.extend(
3186        node.children(&mut cursor)
3187            .collect::<Vec<_>>()
3188            .into_iter()
3189            .rev(),
3190    );
3191    while let Some(child) = stack.pop() {
3192        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3193            break;
3194        }
3195        if matches!(
3196            child.kind(),
3197            "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3198        ) {
3199            let mut fragment_cursor = child.walk();
3200            stack.extend(
3201                child
3202                    .children(&mut fragment_cursor)
3203                    .collect::<Vec<_>>()
3204                    .into_iter()
3205                    .rev(),
3206            );
3207        } else {
3208            tokens.push(child);
3209        }
3210    }
3211    tokens
3212}
3213
3214/// The last identifier before the head ends, per the positional rule.
3215fn export_head_name_from_tokens<'tree>(
3216    tokens: &[Node<'tree>],
3217    source: &str,
3218) -> Option<Node<'tree>> {
3219    let end = tokens
3220        .iter()
3221        .position(|token| {
3222            recovered_export_head_final(*token, source)
3223                || (!token.is_named() && token.kind() == ":")
3224        })
3225        .unwrap_or(tokens.len());
3226    export_head_last_identifier(tokens[..end].iter().copied())
3227}
3228
3229fn export_head_last_identifier<'tree>(
3230    candidates: impl IntoIterator<Item = Node<'tree>>,
3231) -> Option<Node<'tree>> {
3232    candidates
3233        .into_iter()
3234        .filter(|candidate| {
3235            candidate.is_named()
3236                && !candidate.is_missing()
3237                && matches!(
3238                    candidate.kind(),
3239                    "identifier" | "type_identifier" | "field_identifier"
3240                )
3241        })
3242        .last()
3243}
3244
3245/// The `init_declarator` whose `value` carries the class body when the grammar
3246/// keeps a function-like export-macro class head as one `declaration`.
3247fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3248    let mut cursor = declaration.walk();
3249    declaration
3250        .named_children(&mut cursor)
3251        .find(|child| child.kind() == "init_declarator")
3252}
3253
3254/// Read the bases and the initializer-list body of a `declaration`-shaped tail
3255/// of a function-like export-macro class head. The grammar splits a base list
3256/// across bare declarator fields, `ERROR` fragments, and one trailing
3257/// `init_declarator` whose `value` is the class body. `head_end` is where the
3258/// class identity ends and the bases can begin: the end of the access specifier
3259/// when the class name went to a statement label, and the end of the class name
3260/// itself otherwise.
3261fn recovered_export_declaration_tail<'tree>(
3262    declaration: Node<'tree>,
3263    head_end: usize,
3264    source: &str,
3265) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3266    let init = recovered_export_init_declarator(declaration)?;
3267    let body = init.child_by_field_name("value")?;
3268    if body.kind() != "initializer_list" {
3269        return None;
3270    }
3271    let (mut bases, mut facts) =
3272        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3273    let (more_bases, more_facts) =
3274        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3275    bases.extend(more_bases);
3276    facts.extend(more_facts);
3277    Some((bases, (!facts.is_empty()).then_some(facts), body))
3278}
3279
3280/// Whether `node` is the head fragment tree-sitter leaves where
3281/// `class MACRO(args)` was written: the class keyword and the export macro's
3282/// function-like invocation in that order, with nothing else between them.
3283/// Spelling plays no part -- the invocation's `(` following the macro
3284/// identifier directly is what makes it function-like (#2557).
3285///
3286/// Two reductions produce the head. On its own the grammar keeps a body-less
3287/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
3288/// with the invocation's `(`, arguments and `)` beside it. After object-like
3289/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
3290/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
3291/// `declaration` whose class keyword is demoted to a bare `identifier` and
3292/// whose macro invocation becomes the trailing `init_declarator` (#2924).
3293fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3294    match node.kind() {
3295        "ERROR" => {
3296            let Some(class_node) = first_class_like_child(node) else {
3297                return false;
3298            };
3299            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3300                return false;
3301            }
3302            if class_node
3303                .child_by_field_name("name")
3304                .and_then(|name| direct_identifier_name(name, source))
3305                .is_none()
3306            {
3307                return false;
3308            }
3309            class_node
3310                .next_sibling()
3311                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3312        }
3313        "declaration" => {
3314            let mut cursor = node.walk();
3315            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3316            // The class keyword lost its own node: it survives as a bare
3317            // identifier spelled `class`, `struct` or `union`, which is never a
3318            // declarator or a type in well-formed code. It can sit inside an
3319            // `ERROR` beside the object-like macro names, and an `ERROR` here is
3320            // a grouping failure rather than a construct, so read through it.
3321            let Some(keyword) = children
3322                .iter()
3323                .copied()
3324                .flat_map(|child| {
3325                    let mut cursor = child.walk();
3326                    if child.kind() == "ERROR" {
3327                        child.named_children(&mut cursor).collect::<Vec<_>>()
3328                    } else {
3329                        vec![child]
3330                    }
3331                })
3332                .find(|child| {
3333                    child.kind() == "identifier"
3334                        && matches!(node_text(*child, source), "class" | "struct" | "union")
3335                })
3336            else {
3337                return false;
3338            };
3339            children.last().is_some_and(|init| {
3340                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3341                    let mut cursor = init.walk();
3342                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3343                    matches!(parts.as_slice(), [macro_name, arguments]
3344                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
3345                            && arguments.kind() == "argument_list")
3346                }
3347            })
3348        }
3349        _ => false,
3350    }
3351}
3352
3353fn recover_function_like_export_class_pair(
3354    node: Node<'_>,
3355    source: &str,
3356) -> Option<RecoveredFunctionLikeExportClassPair> {
3357    if !is_function_like_export_class_head(node, source) {
3358        return None;
3359    }
3360    let sibling = node.next_named_sibling()?;
3361    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3362        // At translation-unit scope tree-sitter can leave the malformed class
3363        // head in one ERROR node and parse its body as the adjacent compound
3364        // statement. The head still carries the export invocation, displaced
3365        // class name, and optional `final` token in source order, so recover
3366        // the name by the same positional rule as the other shapes.
3367        "compound_statement" => (
3368            recovered_export_pair_head_name(node, sibling, sibling, source)
3369                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3370            None,
3371            None,
3372            sibling,
3373        ),
3374        "expression_statement" => {
3375            let compound = sibling.named_child(0)?;
3376            if compound.kind() != "compound_literal_expression" {
3377                return None;
3378            }
3379            let body = compound.child_by_field_name("value")?;
3380            if body.kind() != "initializer_list" {
3381                return None;
3382            }
3383            (
3384                compound
3385                    .child_by_field_name("type")
3386                    .and_then(|name| direct_identifier_name(name, source))?,
3387                None,
3388                None,
3389                body,
3390            )
3391        }
3392        "labeled_statement" => {
3393            let label = sibling.child_by_field_name("label")?;
3394            if label.kind() != "statement_identifier" {
3395                return None;
3396            }
3397            let name = normalize_cpp_whitespace(node_text(label, source));
3398            let declaration = sibling
3399                .named_children(&mut sibling.walk())
3400                .find(|child| child.kind() == "declaration")?;
3401            let access = declaration.child_by_field_name("type")?;
3402            if !matches!(
3403                node_text(access, source),
3404                "public" | "protected" | "private"
3405            ) {
3406                return None;
3407            }
3408            let (bases, base_facts, body) =
3409                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3410            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3411        }
3412        // `class MACRO(2, 0) Name final { ... };`,
3413        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3414        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3415        // spread over the `type` field, the declarator field, and `ERROR`
3416        // fragments beside them; the name is the last one before the head ends.
3417        "function_definition" => {
3418            let body = sibling.child_by_field_name("body")?;
3419            if body.kind() != "compound_statement" {
3420                return None;
3421            }
3422            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3423            let (bases, base_facts) = recovered_export_head_base_data(
3424                sibling,
3425                name_node.end_byte(),
3426                body.start_byte(),
3427                source,
3428            );
3429            (
3430                normalize_cpp_whitespace(node_text(name_node, source)),
3431                (!bases.is_empty()).then_some(bases),
3432                (!base_facts.is_empty()).then_some(base_facts),
3433                body,
3434            )
3435        }
3436        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3437        // comma-separated base list makes the grammar keep the whole tail as one
3438        // declaration whose trailing `init_declarator` carries the body.
3439        "declaration" => {
3440            let init = recovered_export_init_declarator(sibling)?;
3441            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3442            let (bases, base_facts, body) =
3443                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3444            (
3445                normalize_cpp_whitespace(node_text(name_node, source)),
3446                (!bases.is_empty()).then_some(bases),
3447                base_facts,
3448                body,
3449            )
3450        }
3451        _ => return None,
3452    };
3453    debug_assert!(
3454        !name.is_empty(),
3455        "a recovered class head names its class by an identifier token"
3456    );
3457    let range = Range {
3458        start_byte: node.start_byte(),
3459        end_byte: sibling.end_byte(),
3460        start_line: node.start_position().row + 1,
3461        end_line: sibling.end_position().row + 1,
3462    };
3463    Some(RecoveredFunctionLikeExportClassPair {
3464        name,
3465        raw_supertypes,
3466        base_facts,
3467        range,
3468        fragmented_body: recovered_fragmented_export_body(body, range)?,
3469    })
3470}
3471
3472/// Recover a function-like export-macro class that tree-sitter embedded in a
3473/// larger error after an earlier malformed class body. The grammar still
3474/// preserves every part of the class head: the `class` token, export macro
3475/// identifier and argument list, displaced class identifier, access specifier,
3476/// base field, and initializer-list-shaped body. Match only that complete
3477/// structured sequence and keep each recovered class's exact byte envelope.
3478fn recover_embedded_function_like_export_classes(
3479    node: Node<'_>,
3480    source: &str,
3481) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3482    if !node.is_error() {
3483        return Vec::new();
3484    }
3485
3486    let mut nodes = Vec::new();
3487    let mut class_tokens = Vec::new();
3488    let mut stack = vec![node];
3489    while let Some(current) = stack.pop() {
3490        nodes.push(current);
3491        // An error subtree without an anonymous class token cannot contain
3492        // a recoverable class head. Detect that before sorting or resolving
3493        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3494        if !current.is_named() && current.kind() == "class" {
3495            class_tokens.push(current);
3496        }
3497        push_children_reversed(current, &mut stack);
3498    }
3499    if class_tokens.is_empty() {
3500        return Vec::new();
3501    }
3502    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3503    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3504
3505    // The scans below ask the same handful of kind questions once per node of
3506    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3507    let language = node.language();
3508    let class_kind = NodeKindIds::new(&language, "class");
3509    let identifier_kinds = [
3510        NodeKindIds::new(&language, "identifier"),
3511        NodeKindIds::new(&language, "type_identifier"),
3512        NodeKindIds::new(&language, "field_identifier"),
3513    ];
3514    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3515    let colon_kind = NodeKindIds::new(&language, ":");
3516    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3517
3518    let mut recovered = Vec::new();
3519    for class_token in class_tokens {
3520        debug_assert!(class_kind.matches(class_token));
3521        let row = class_token.start_position().row;
3522        // The head after the `class` token reads `MACRO(args) macros... name
3523        // [final] : bases`. The macro is the first identifier, a function-like
3524        // invocation when its argument list is the next thing after it, and the
3525        // name is the last identifier before the head ends at `final` or at the
3526        // base clause `:`. Spelling plays no part (#2557).
3527        let is_identifier = |candidate: &Node<'_>| {
3528            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3529        };
3530        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3531            candidate.start_byte() >= class_token.end_byte()
3532                && candidate.start_position().row == row
3533                && is_identifier(candidate)
3534        }) else {
3535            continue;
3536        };
3537        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3538            argument_list_kind.matches(*candidate)
3539                && candidate.start_byte() >= macro_name.end_byte()
3540                && candidate.start_position().row == row
3541        }) else {
3542            continue;
3543        };
3544        if nodes.iter().any(|candidate| {
3545            is_identifier(candidate)
3546                && candidate.start_byte() >= macro_name.end_byte()
3547                && candidate.end_byte() <= arguments.start_byte()
3548        }) {
3549            continue;
3550        }
3551        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3552            candidate.start_byte() >= arguments.end_byte()
3553                && (recovered_export_head_final(*candidate, source)
3554                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3555        }) else {
3556            continue;
3557        };
3558        let Some(name_node) =
3559            export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3560                is_identifier(candidate)
3561                    && candidate.start_byte() >= arguments.end_byte()
3562                    && candidate.end_byte() <= head_end.start_byte()
3563                    && candidate.start_position().row == row
3564            }))
3565        else {
3566            continue;
3567        };
3568        let name = normalize_cpp_whitespace(node_text(name_node, source));
3569        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3570            field_initializer_kind.matches(*candidate)
3571                && candidate.start_byte() >= name_node.end_byte()
3572                && candidate
3573                    .child_by_field_name("field")
3574                    .or_else(|| candidate.named_child(0))
3575                    .is_some()
3576                && candidate
3577                    .child_by_field_name("value")
3578                    .or_else(|| {
3579                        let mut cursor = candidate.walk();
3580                        candidate
3581                            .named_children(&mut cursor)
3582                            .find(|child| child.kind() == "initializer_list")
3583                    })
3584                    .is_some_and(|value| value.kind() == "initializer_list")
3585        }) else {
3586            continue;
3587        };
3588        let has_access = nodes.iter().copied().any(|candidate| {
3589            candidate.start_byte() >= name_node.end_byte()
3590                && candidate.end_byte() <= base_initializer.start_byte()
3591                && matches!(
3592                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3593                    "public" | "protected" | "private"
3594                )
3595        });
3596        if !has_access {
3597            continue;
3598        }
3599        let Some(base_node) = base_initializer
3600            .child_by_field_name("field")
3601            .or_else(|| base_initializer.named_child(0))
3602        else {
3603            continue;
3604        };
3605        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3606            continue;
3607        };
3608        let body = base_initializer
3609            .child_by_field_name("value")
3610            .or_else(|| {
3611                let mut cursor = base_initializer.walk();
3612                base_initializer
3613                    .named_children(&mut cursor)
3614                    .find(|child| child.kind() == "initializer_list")
3615            })
3616            .expect("initializer-list value checked above");
3617        let range = Range {
3618            start_byte: class_token.start_byte(),
3619            end_byte: body.end_byte(),
3620            start_line: class_token.start_position().row + 1,
3621            end_line: body.end_position().row + 1,
3622        };
3623        if recovered
3624            .iter()
3625            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3626                existing.name == name && existing.range == range
3627            })
3628        {
3629            continue;
3630        }
3631        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3632            continue;
3633        };
3634        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3635            name,
3636            range,
3637            raw_supertypes: vec![base],
3638            base_facts: vec![base_fact],
3639            fragmented_body: match recovered_fragmented_export_body(body, range) {
3640                Some(fragmented) => fragmented,
3641                None => continue,
3642            },
3643        });
3644    }
3645    recovered
3646}
3647
3648fn lifted_function_like_export_class_namespace<'tree>(
3649    node: Node<'tree>,
3650    source: &str,
3651    ancestry: &ParentIndex<'tree>,
3652) -> Option<String> {
3653    // A long malformed body can embed the next exported class several levels
3654    // below a bogus top-level function_definition. Compare namespace evidence
3655    // against that top-level envelope, not only the recovered ERROR's direct
3656    // parent. The source tree still proves the same boundary: one earlier
3657    // malformed namespace and one later standalone closing brace.
3658    let mut anchor = node;
3659    let parent = loop {
3660        let parent = ancestry.parent(anchor)?;
3661        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3662            break parent;
3663        }
3664        anchor = parent;
3665    };
3666    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3667        sibling.start_byte() > anchor.end_byte()
3668            && sibling.kind() == "ERROR"
3669            && sibling.named_child_count() == 0
3670            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3671    });
3672    if !has_later_close {
3673        return None;
3674    }
3675    let candidates = parent
3676        .named_children(&mut parent.walk())
3677        .filter(|sibling| {
3678            sibling.kind() == "namespace_definition"
3679                && sibling.has_error()
3680                && sibling.end_byte() < anchor.start_byte()
3681        })
3682        .filter_map(|namespace| {
3683            namespace
3684                .child_by_field_name("name")
3685                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3686                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3687        })
3688        .collect::<Vec<_>>();
3689    let [namespace] = candidates.as_slice() else {
3690        return None;
3691    };
3692    Some(namespace.clone())
3693}
3694
3695pub(crate) fn recovered_function_like_export_class_pair_has_body(
3696    node: Node<'_>,
3697    source: &str,
3698    identifier: &str,
3699    range: &Range,
3700) -> bool {
3701    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3702        recovered.name == identifier
3703            && recovered.range.start_byte == range.start_byte
3704            && recovered.range.end_byte == range.end_byte
3705    })
3706}
3707
3708fn recovered_exported_class_has_body(
3709    node: Node<'_>,
3710    source: &str,
3711    expected_name: &str,
3712) -> Option<bool> {
3713    match node.kind() {
3714        "function_definition" => {
3715            let (class_node, name, _, _) =
3716                recover_exported_class_function_definition(node, source)?;
3717            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3718        }
3719        "declaration" | "field_declaration" => {
3720            let recovered = recover_exported_class_declaration(node, source)?;
3721            (recovered.name == expected_name).then(|| recovered.body.is_some())
3722        }
3723        _ => None,
3724    }
3725}
3726
3727/// One file's embedded export-macro class recovery, resolved once and keyed by
3728/// the `ERROR` node that mints each set.
3729///
3730/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3731/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3732/// once per class-like unit in the file. On a translation unit the parser could
3733/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3734/// node spans most of the file -- that is one full subtree pass per class,
3735/// quadratic in the file's size, and it was 78% of that file's inverse scan
3736/// (#1496).
3737///
3738/// Keyed by byte span rather than node identity, so one analyzer generation's
3739/// re-parses of the same content share the index. A nested `ERROR` that shares
3740/// its parent's span recovers the same classes: the only node the parent adds
3741/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3742#[derive(Default)]
3743pub struct CppRecoveredExportClassIndex {
3744    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3745}
3746
3747impl CppRecoveredExportClassIndex {
3748    pub fn build(root: Node<'_>, source: &str) -> Self {
3749        let mut by_error_node: HashMap<
3750            (usize, usize),
3751            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3752        > = HashMap::default();
3753        // One cursor for the whole-file walk, and the error question asked by
3754        // symbol rather than by `kind()` string (#3097).
3755        let mut cursor = root.walk();
3756        let mut stack = vec![root];
3757        while let Some(node) = stack.pop() {
3758            if node.is_error() {
3759                let recovered = recover_embedded_function_like_export_classes(node, source);
3760                if !recovered.is_empty() {
3761                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3762                }
3763            }
3764            stack.extend(node.named_children(&mut cursor));
3765        }
3766        Self { by_error_node }
3767    }
3768
3769    /// The bytes this index holds, for the analyzer cache's weight.
3770    pub fn approximate_size(&self) -> usize {
3771        self.by_error_node
3772            .values()
3773            .fold(0usize, |total, recovered| {
3774                recovered.iter().fold(
3775                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3776                    |acc, class| {
3777                        acc.saturating_add(std::mem::size_of::<
3778                            RecoveredEmbeddedFunctionLikeExportClass,
3779                        >())
3780                        .saturating_add(class.name.len())
3781                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3782                    },
3783                )
3784            })
3785    }
3786
3787    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3788        self.by_error_node
3789            .get(&(node.start_byte(), node.end_byte()))
3790            .is_some_and(|recovered| {
3791                recovered.iter().any(|class| {
3792                    class.name == identifier
3793                        && class.range.start_byte == range.start_byte
3794                        && class.range.end_byte == range.end_byte
3795                })
3796            })
3797    }
3798}
3799
3800// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3801// `recovered_class_body_at` pops while deciding whether a recovered class shape
3802// claims one declaration range. The count is deterministic for a given source,
3803// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3804// pins it directly instead of timing the walk, the way #2358 pinned the
3805// `remove_code_unit` scan.
3806#[cfg(any(test, feature = "test-support"))]
3807thread_local! {
3808    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3809        const { std::cell::Cell::new(0) };
3810}
3811
3812/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3813/// the calling thread since the last
3814/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3815#[cfg(any(test, feature = "test-support"))]
3816#[doc(hidden)]
3817pub fn recovered_class_body_node_visits_for_test() -> usize {
3818    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3819}
3820
3821/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3822#[cfg(any(test, feature = "test-support"))]
3823#[doc(hidden)]
3824pub fn reset_recovered_class_body_node_visits_for_test() {
3825    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3826}
3827
3828#[cfg(any(test, feature = "test-support"))]
3829fn record_recovered_class_body_visit() {
3830    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3831}
3832
3833#[cfg(not(any(test, feature = "test-support")))]
3834fn record_recovered_class_body_visit() {}
3835
3836/// Whether a recovered class shape named `identifier` owns `range`, and if so
3837/// whether that shape has a body.
3838///
3839/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3840/// forward declaration, and `None` means no recovered shape claims the range,
3841/// so the caller reads the plain `class_specifier` family instead. This is the
3842/// single definition of "does a recovered class have a body": the resolver's
3843/// declaration-strength answer and the navigation occurrence role both read it,
3844/// so an export-macro class is a definition on both paths.
3845///
3846/// The walk follows only the nodes whose span covers `range.start_byte`, which
3847/// is every node that can answer. Each recovered shape reports a range that
3848/// starts at the node's own start byte (the function-like export pair, whose
3849/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3850/// class, which the caller gates on an equal start) or at a token inside the
3851/// node (the embedded export class, keyed on its `class` token, and the
3852/// exported class wrapper, gated on containment here), and every one of them is
3853/// accepted only on an exact match with `range`. Descending everywhere instead
3854/// made one declaration-strength question cost a full pass over the file, so
3855/// asking it once per reference was quadratic in file size: 80% of the 385 s
3856/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3857/// (#1496).
3858pub(crate) fn recovered_class_body_at(
3859    recovered_export_classes: &CppRecoveredExportClassIndex,
3860    root: Node<'_>,
3861    source: &str,
3862    identifier: &str,
3863    range: &Range,
3864) -> Option<bool> {
3865    let covers_range_start = |node: &Node<'_>| {
3866        node.start_byte() <= range.start_byte
3867            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3868    };
3869    let mut stack = vec![root];
3870    let mut saw_forward = false;
3871    while let Some(node) = stack.pop() {
3872        record_recovered_class_body_visit();
3873        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3874        // so an unequal start settles it before the recovery reads the node's
3875        // children at all.
3876        if (node.start_byte() == range.start_byte
3877            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3878            || recovered_export_classes.claims(node, identifier, range)
3879            || (node.start_byte() == range.start_byte
3880                && recovered_fragmented_class_has_body(node, source, identifier, range))
3881        {
3882            return Some(true);
3883        }
3884        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3885            return Some(true);
3886        }
3887        // Macro-decorated exported classes are recovered from a malformed
3888        // function_definition/declaration wrapper. Their indexed class range starts at
3889        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3890        // may also extend the indexed range beyond the wrapper through trailing class
3891        // fragments. Match the structured container that owns the range start by its
3892        // recovered name instead of requiring identical boundaries.
3893        if node.start_byte() <= range.start_byte
3894            && range.start_byte < node.end_byte()
3895            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3896        {
3897            if has_body {
3898                return Some(true);
3899            }
3900            saw_forward = true;
3901            continue;
3902        }
3903        let mut cursor = node.walk();
3904        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3905    }
3906    saw_forward.then_some(false)
3907}
3908
3909/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3910/// head of `identifier`'s member list at `range`.
3911///
3912/// An aggregate whose member list opens with a field-list macro invocation
3913/// never forms a specifier, so the ordinary strength read finds no specifier
3914/// node at the indexed range and answers `Unknown`. A complete definition
3915/// recovered this way then loses the defining-type choice to a forward
3916/// declaration of the same tag in another header, and every member the
3917/// definition declares becomes unreachable through it: libuv's
3918/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3919/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3920/// proves the body, so it answers for its own ranges.
3921fn recovered_collapsed_aggregate_has_body(
3922    node: Node<'_>,
3923    source: &str,
3924    identifier: &str,
3925    range: &Range,
3926) -> bool {
3927    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3928        head.key.start_byte() == range.start_byte
3929            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3930    };
3931    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3932        return true;
3933    }
3934    if !node.is_error() {
3935        return false;
3936    }
3937    let mut cursor = node.walk();
3938    let children = node.children(&mut cursor).collect::<Vec<_>>();
3939    children
3940        .iter()
3941        .enumerate()
3942        .filter(|(_, child)| {
3943            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3944        })
3945        .any(|(index, _)| {
3946            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3947        })
3948}
3949
3950/// Whether `node` is the base type displaced into the declarator field of an
3951/// export-macro class that tree-sitter represented as a declaration or
3952/// function definition.
3953///
3954/// Declaration extraction already recovers this exact malformed envelope as a
3955/// class and records the declarator as its base. Reference extraction must use
3956/// the same structural fact instead of treating the node as a function name.
3957pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3958    if !matches!(
3959        node.kind(),
3960        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3961    ) {
3962        return false;
3963    }
3964    if let Some(function) = node.parent().filter(|parent| {
3965        parent.kind() == "function_definition"
3966            && parent
3967                .child_by_field_name("declarator")
3968                .is_some_and(|declarator| same_node(declarator, node))
3969    }) {
3970        return recover_exported_class_function_definition(function, source)
3971            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
3972    }
3973    let Some(initializer) = node.parent().filter(|parent| {
3974        parent.kind() == "init_declarator"
3975            && parent
3976                .child_by_field_name("declarator")
3977                .is_some_and(|declarator| same_node(declarator, node))
3978    }) else {
3979        return false;
3980    };
3981    initializer
3982        .parent()
3983        .filter(|parent| parent.kind() == "declaration")
3984        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3985        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3986}
3987
3988/// Recover the class item from a region reparse that still carries the
3989/// sentinel's synthetic function envelope.  An unknown class attribute can
3990/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3991/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3992/// then nested below that function, so direct class-child lookup is not enough.
3993struct CppSentinelReparsedClass<'tree> {
3994    declaration_node: Node<'tree>,
3995    name: String,
3996    body: Node<'tree>,
3997    raw_supertypes: Option<Vec<String>>,
3998    base_facts: Option<Vec<CppBaseSpecifierFact>>,
3999}
4000
4001fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4002    let mut cursor = root.walk();
4003    root.named_children(&mut cursor)
4004        .find(|child| child.kind() != "comment")
4005        .filter(|child| child.kind() == "template_declaration")
4006}
4007
4008fn cpp_sentinel_reparsed_class<'tree>(
4009    root: Node<'tree>,
4010    template_node: Option<Node<'tree>>,
4011    source: &str,
4012    ancestry: &ParentIndex<'tree>,
4013) -> Option<CppSentinelReparsedClass<'tree>> {
4014    let container = template_node.unwrap_or(root);
4015    let mut cursor = container.walk();
4016    for child in container.named_children(&mut cursor) {
4017        if matches!(
4018            child.kind(),
4019            "class_specifier" | "struct_specifier" | "union_specifier"
4020        ) {
4021            let name = class_like_name(child, source, ancestry)?;
4022            let body = cpp_body_node(child)?;
4023            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4024                .then(|| extract_cpp_supertypes(child, source));
4025            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4026                .then(|| extract_cpp_base_facts(child, source));
4027            return Some(CppSentinelReparsedClass {
4028                declaration_node: child,
4029                name,
4030                body,
4031                raw_supertypes,
4032                base_facts,
4033            });
4034        }
4035        if child.kind() == "declaration"
4036            && let Some(class_node) = first_class_like_child(child)
4037        {
4038            let name = class_like_name(class_node, source, ancestry)?;
4039            let body = cpp_body_node(class_node)?;
4040            let raw_supertypes =
4041                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4042                    .then(|| extract_cpp_supertypes(class_node, source));
4043            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4044                .then(|| extract_cpp_base_facts(class_node, source));
4045            return Some(CppSentinelReparsedClass {
4046                declaration_node: class_node,
4047                name,
4048                body,
4049                raw_supertypes,
4050                base_facts,
4051            });
4052        }
4053        // Only when the nested class item carries its own body. A bodyless
4054        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
4055        // a function definition -- is the export-macro shape recovered by the
4056        // next arm, and must fall through to it rather than abort the search.
4057        if child.kind() == "function_definition"
4058            && let Some(class_node) = first_class_like_child(child)
4059            && let Some(body) = cpp_body_node(class_node)
4060            && let Some(name) = class_like_name(class_node, source, ancestry)
4061        {
4062            let raw_supertypes =
4063                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4064                    .then(|| extract_cpp_supertypes(class_node, source));
4065            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4066                .then(|| extract_cpp_base_facts(class_node, source));
4067            return Some(CppSentinelReparsedClass {
4068                declaration_node: class_node,
4069                name,
4070                body,
4071                raw_supertypes,
4072                base_facts,
4073            });
4074        }
4075        if child.kind() == "function_definition"
4076            && let Some((_, name, raw_supertypes, base_facts)) =
4077                recover_exported_class_function_definition(child, source)
4078        {
4079            let body = cpp_body_node(child)?;
4080            return Some(CppSentinelReparsedClass {
4081                declaration_node: child,
4082                name,
4083                body,
4084                raw_supertypes,
4085                base_facts,
4086            });
4087        }
4088    }
4089    None
4090}
4091
4092fn recovered_postfix_export_macro_base(
4093    node: Node<'_>,
4094    type_node: Node<'_>,
4095    declarator: Node<'_>,
4096    source: &str,
4097) -> Option<String> {
4098    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4099        .and_then(|base| recovered_malformed_base_name(base, source))
4100}
4101
4102fn recovered_postfix_export_macro_base_node<'tree>(
4103    node: Node<'tree>,
4104    type_node: Node<'tree>,
4105    declarator: Node<'tree>,
4106    source: &str,
4107) -> Option<Node<'tree>> {
4108    let mut cursor = node.walk();
4109    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4110        child.kind() == "ERROR"
4111            && child.start_byte() >= type_node.end_byte()
4112            && child.end_byte() <= declarator.start_byte()
4113            && postfix_export_macro_inheritance(*child, source)
4114    });
4115    malformed_clauses.next()?;
4116    if malformed_clauses.next().is_some() {
4117        return None;
4118    }
4119    Some(declarator)
4120}
4121
4122fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4123    let mut macro_count = 0;
4124    let mut colon_count = 0;
4125    let mut access_count = 0;
4126    for index in 0..node.child_count() {
4127        let Some(child) = node.child(index) else {
4128            return false;
4129        };
4130        match child.kind() {
4131            "identifier" | "type_identifier" if child.is_named() => {
4132                let candidate = normalize_cpp_whitespace(node_text(child, source));
4133                if !cpp_export_macro_token(&candidate) {
4134                    return false;
4135                }
4136                macro_count += 1;
4137            }
4138            ":" if !child.is_named() => colon_count += 1,
4139            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4140            _ => return false,
4141        }
4142    }
4143    macro_count == 1 && colon_count == 1 && access_count == 1
4144}
4145
4146fn recovered_single_base_after_declarator_node<'tree>(
4147    node: Node<'tree>,
4148    declarator: Node<'tree>,
4149    source: &str,
4150) -> Option<Node<'tree>> {
4151    let body_start = node
4152        .child_by_field_name("body")
4153        .map(|body| body.start_byte())
4154        .unwrap_or(node.end_byte());
4155    let mut cursor = node.walk();
4156    let mut bases = node
4157        .named_children(&mut cursor)
4158        .filter(|child| {
4159            child.kind() == "ERROR"
4160                && child.start_byte() >= declarator.end_byte()
4161                && child.end_byte() <= body_start
4162        })
4163        .filter_map(|error| {
4164            displaced_exported_class_name(error, source)?;
4165            let mut error_cursor = error.walk();
4166            error
4167                .named_children(&mut error_cursor)
4168                .find(|child| recovered_malformed_base_name(*child, source).is_some())
4169        });
4170    let base = bases.next()?;
4171    bases.next().is_none().then_some(base)
4172}
4173
4174fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4175    (0..node.child_count()).any(|index| {
4176        node.child(index).is_some_and(|child| {
4177            child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4178        })
4179    })
4180}
4181
4182fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4183    matches!(name, "public" | "protected" | "private")
4184}
4185
4186pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4187    recover_exported_class_function_definition(node, source).is_some()
4188}
4189
4190fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4191    matches!(
4192        kind,
4193        "ERROR"
4194            | "preproc_if"
4195            | "preproc_ifdef"
4196            | "preproc_ifndef"
4197            | "preproc_else"
4198            | "preproc_elif"
4199    ) || (kind == "labeled_statement" && in_class_scope)
4200}
4201
4202pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4203    if node.kind() != "declaration" {
4204        return false;
4205    }
4206    let mut ancestor = node.parent();
4207    while let Some(container) = ancestor {
4208        match container.kind() {
4209            "compound_statement" => {
4210                return container.parent().is_some_and(|class_container| {
4211                    is_recovered_exported_class_container(class_container, source)
4212                });
4213            }
4214            // These containers preserve ScopeInfo in visit_node. declaration_list is
4215            // the body container selected for a linkage specification.
4216            "template_declaration" | "linkage_specification" | "declaration_list" => {}
4217            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4218            _ => return false,
4219        }
4220        ancestor = container.parent();
4221    }
4222    false
4223}
4224
4225fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4226    let body_start = node
4227        .child_by_field_name("body")
4228        .map(|body| body.start_byte())
4229        .unwrap_or(node.end_byte());
4230    let mut stack = Vec::new();
4231    for index in (0..node.named_child_count()).rev() {
4232        let Some(child) = node.named_child(index) else {
4233            continue;
4234        };
4235        if child.start_byte() >= body_start {
4236            continue;
4237        }
4238        stack.push(child);
4239    }
4240
4241    let mut best = None;
4242    while let Some(current) = stack.pop() {
4243        if matches!(current.kind(), "identifier" | "type_identifier") {
4244            let name = normalize_cpp_whitespace(node_text(current, source));
4245            if !name.is_empty()
4246                && !cpp_export_macro_token(&name)
4247                && !matches!(name.as_str(), "class" | "struct" | "union")
4248            {
4249                best = Some(name);
4250            }
4251            continue;
4252        }
4253
4254        for index in (0..current.named_child_count()).rev() {
4255            if let Some(child) = current.named_child(index)
4256                && child.start_byte() < body_start
4257            {
4258                stack.push(child);
4259            }
4260        }
4261    }
4262    best
4263}
4264
4265fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4266    if node.kind() == "declaration"
4267        && node
4268            .child_by_field_name("type")
4269            .or_else(|| first_class_like_child(node))
4270            .is_some_and(|type_node| {
4271                matches!(
4272                    type_node.kind(),
4273                    "class_specifier" | "struct_specifier" | "union_specifier"
4274                )
4275            })
4276        && let Some(name) = node
4277            .child_by_field_name("declarator")
4278            .and_then(|declarator| declarator_name_from_node(declarator, source))
4279        && !cpp_export_macro_token(&name)
4280    {
4281        return Some(name);
4282    }
4283
4284    if node.kind() == "function_definition"
4285        && node.child_by_field_name("type").is_some_and(|type_node| {
4286            matches!(
4287                type_node.kind(),
4288                "class_specifier" | "struct_specifier" | "union_specifier"
4289            )
4290        })
4291        && let Some(name) = node
4292            .child_by_field_name("declarator")
4293            .and_then(|declarator| direct_identifier_name(declarator, source))
4294        && !cpp_export_macro_token(&name)
4295    {
4296        return Some(name);
4297    }
4298
4299    let class_node = if matches!(
4300        node.kind(),
4301        "class_specifier" | "struct_specifier" | "union_specifier"
4302    ) {
4303        node
4304    } else {
4305        first_class_like_child(node)?
4306    };
4307    class_like_name_from_children(class_node, source)
4308}
4309
4310/// Normalize the text of a directly selected C++ identifier node.
4311pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4312    if !matches!(
4313        node.kind(),
4314        "identifier" | "field_identifier" | "type_identifier"
4315    ) {
4316        return None;
4317    }
4318    let name = normalize_cpp_whitespace(node_text(node, source));
4319    (!name.is_empty()).then_some(name)
4320}
4321
4322fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4323    if let Some(name) = direct_identifier_name(node, source) {
4324        return Some(name);
4325    }
4326    let mut cursor = node.walk();
4327    node.named_children(&mut cursor)
4328        .find_map(|child| declarator_name_from_node(child, source))
4329}
4330
4331fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4332    let mut cursor = node.walk();
4333    node.named_children(&mut cursor).find(|child| {
4334        matches!(
4335            child.kind(),
4336            "class_specifier" | "struct_specifier" | "union_specifier"
4337        )
4338    })
4339}
4340
4341/// Push a container's children as a `Siblings` cursor rather than snapshotting
4342/// them all with one shared scope: children are visited one at a time so a
4343/// `using namespace X;` sibling can affect the scope threaded to the siblings
4344/// that textually follow it (issue #1093).
4345fn push_cpp_container_work<'tree>(
4346    node: Node<'tree>,
4347    scope: ScopeInfo,
4348    stack: &mut Vec<CppWork<'tree>>,
4349) {
4350    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4351}
4352
4353/// Materialize one selected named-child range with a tree-sitter cursor. The
4354/// cursor advances linearly across the parent's concrete children; repeatedly
4355/// asking for `named_child(index)` is quadratic on very wide generated nodes.
4356fn push_cpp_sibling_range<'tree>(
4357    parent: Node<'tree>,
4358    start_index: usize,
4359    end_index: usize,
4360    scope: ScopeInfo,
4361    stack: &mut Vec<CppWork<'tree>>,
4362) {
4363    let mut cursor = parent.walk();
4364    let children = parent
4365        .named_children(&mut cursor)
4366        .skip(start_index)
4367        .take(end_index.saturating_sub(start_index))
4368        .collect::<Vec<_>>()
4369        .into_iter();
4370    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4371}
4372
4373/// Advance a `Siblings` cursor by one child: dispatch the current child under
4374/// the scope accumulated from its *earlier* siblings, then push a
4375/// continuation for the remaining siblings carrying the scope updated for
4376/// *this* child (only `using namespace X;` directives change it). Pushing the
4377/// continuation before the current child's own node work means the current
4378/// child's subtree fully drains (LIFO) before the next sibling is visited,
4379/// preserving left-to-right order.
4380fn advance_cpp_siblings<'tree>(
4381    mut siblings: CppSiblingsWork<'tree>,
4382    source: &str,
4383    stack: &mut Vec<CppWork<'tree>>,
4384) {
4385    let Some(child) = siblings.children.next() else {
4386        return;
4387    };
4388    let current_scope = siblings.scope.clone();
4389    if let Some(namespace) = cpp_using_namespace_target(child, source) {
4390        siblings.scope.visible_using_namespaces.push(namespace);
4391    }
4392    if !siblings.children.as_slice().is_empty() {
4393        stack.push(CppWork::Siblings(siblings));
4394    }
4395    stack.push(CppWork::Node(CppNodeWork {
4396        node: child,
4397        scope: current_scope,
4398    }));
4399}
4400
4401/// The namespace target of a `using namespace X;` directive, or `None` for
4402/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
4403/// kind. Distinguished structurally by the presence of the grammar's literal
4404/// `namespace` keyword token among the node's children -- not by inspecting
4405/// source text -- so it never misreads a member-importing using-declaration
4406/// as a namespace directive.
4407fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4408    if node.kind() != "using_declaration" {
4409        return None;
4410    }
4411    let mut cursor = node.walk();
4412    let is_namespace_directive = node
4413        .children(&mut cursor)
4414        .any(|child| child.kind() == "namespace");
4415    if !is_namespace_directive {
4416        return None;
4417    }
4418    let target = node.named_child(0)?;
4419    // A leading `::` is the explicit-global marker, not part of the namespace
4420    // path (`using namespace ::std::chrono;`). Drop that AST token before
4421    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4422    // keeps: storing the marker verbatim desynced the legacy package string from
4423    // the FqName bridge, which splits on `::` and drops the empty leading
4424    // component, tripping the package/short boundary assert when a bare-owner
4425    // out-of-line definition borrowed the directive's namespace (#1093 path).
4426    let start = target
4427        .child(0)
4428        .filter(|child| !child.is_named() && child.kind() == "::")
4429        .map_or(target.start_byte(), |marker| marker.end_byte());
4430    let text = normalize_cpp_whitespace(
4431        source
4432            .get(start..target.end_byte())
4433            .expect("using-directive target covers one source range"),
4434    );
4435    (!text.is_empty()).then_some(text)
4436}
4437
4438/// Tokens that open a construct only a matching later token closes.
4439const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4440
4441/// The opening tokens of the constructs a C/C++ parse left open at the end of
4442/// the file, in source order (#3501). Empty unless the tree's root is an ERROR
4443/// node.
4444///
4445/// The grammar has no preprocessor, so a construct opened in one conditional
4446/// branch and closed in another stays open: `#if 0` / `extern "C" {` /
4447/// `#endif` leaves its `{` unmatched until a later `#if 0` / `}` / `#endif`.
4448/// One such pair repairs with a single MISSING token. With more of them, the
4449/// parse version tree-sitter keeps reaches the end of input in its error
4450/// state, and tree-sitter wraps every subtree on that version's stack in one
4451/// ERROR node and returns it as the root (`recover_eof` in its `parser.c`).
4452/// The root's children are then exactly that stack: completed subtrees, and
4453/// the bare tokens of the constructs still waiting to be reduced when input
4454/// ran out. A reduced construct is a node of its own, so a bare opener among
4455/// the root's children is one the parse never closed. tdlib/td's copy of the
4456/// SQLite amalgamation parses this way: its root spans all 8,274,268 bytes,
4457/// and its outermost open construct is the `#ifndef SQLITEINT_H` include guard
4458/// at byte 19,718.
4459pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4460    assert!(
4461        root.parent().is_none(),
4462        "only a tree's root records what its parse left open"
4463    );
4464    if !root.is_error() {
4465        return Vec::new();
4466    }
4467    children_iter(root)
4468        .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4469        .collect()
4470}
4471
4472/// Record every declaration of `parsed` that has a range inside `recovery`
4473/// as recovered, unless a narrower recovery already claimed it, and mark the
4474/// reading partial when any declaration lies there (#3501).
4475///
4476/// `recovery` runs from the outermost construct the parse left open to the
4477/// end of the file. Everything before that construct was completed at file
4478/// scope, as in any parse. Everything after it sat on the parse stack above
4479/// an open construct when input ran out, so the scope and nesting the walk
4480/// gave it are a recovery claim rather than a reading of a completed tree,
4481/// and the file's analysis is partial whichever recovery recorded it.
4482fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4483    let recorded: HashSet<CodeUnit> = parsed
4484        .materialization_records
4485        .iter()
4486        .filter_map(|record| match record {
4487            MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4488            _ => None,
4489        })
4490        .collect();
4491    let mut left_open: Vec<(usize, CodeUnit)> = parsed
4492        .declarations()
4493        .iter()
4494        .filter_map(|unit| {
4495            let ranges = parsed.declaration_ranges(unit);
4496            let first = ranges.iter().map(|range| range.start_byte).min()?;
4497            let last = ranges.iter().map(|range| range.start_byte).max()?;
4498            (last >= recovery.start_byte).then(|| (first, unit.clone()))
4499        })
4500        .collect();
4501    if left_open.is_empty() {
4502        return;
4503    }
4504    parsed.completeness = ParseCompleteness::Partial;
4505    left_open.retain(|(_, unit)| !recorded.contains(unit));
4506    left_open.sort();
4507    for (_, unit) in left_open {
4508        parsed
4509            .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4510    }
4511}
4512
4513/// Extract all requested dialect readings from one primary AST event stream.
4514/// Declaration schedulers retain their own admission, recovery and sibling order;
4515/// structural facts, includes and identifiers are interpreted once for the tree.
4516pub(crate) fn parse_cpp_readings<'tree>(
4517    file: &ProjectFile,
4518    source: &str,
4519    root: Node<'tree>,
4520    ancestry: &ParentIndex<'tree>,
4521    dialects: &[LanguageDialect],
4522) -> Vec<ParsedFile> {
4523    parse_cpp_readings_with_object_macro_fields(
4524        file,
4525        source,
4526        root,
4527        ancestry,
4528        dialects,
4529        HashMap::default(),
4530    )
4531}
4532
4533pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4534    file: &ProjectFile,
4535    source: &str,
4536    root: Node<'tree>,
4537    ancestry: &ParentIndex<'tree>,
4538    dialects: &[LanguageDialect],
4539    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4540) -> Vec<ParsedFile> {
4541    assert!(
4542        dialects.len() == 1
4543            || (dialects.len() == 2
4544                && dialects[0] != LanguageDialect::CppC
4545                && dialects[1] == LanguageDialect::CppC),
4546        "one primary dialect, optionally followed by its C alternative"
4547    );
4548    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4549    let properties = RefCell::new(HashMap::default());
4550    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4551    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4552    let mut using_namespaces = Vec::new();
4553    let mut readings: Vec<_> = dialects
4554        .iter()
4555        .map(|_| ParsedFile::new(String::new()))
4556        .collect();
4557    let mut visitors: Vec<_> = readings
4558        .iter_mut()
4559        .zip(dialects)
4560        .map(|(parsed, dialect)| CppVisitor {
4561            file,
4562            source,
4563            parsed,
4564            source_collector: &collector,
4565            source_properties: &properties,
4566            source_dependencies: &dependencies,
4567            primary_ancestry: ancestry,
4568            primary_root_id: root.id(),
4569            source_links: HashMap::default(),
4570            source_declarations: HashSet::default(),
4571            preprocessor_context_cache: Default::default(),
4572            c_tag_semantics: *dialect == LanguageDialect::CppC,
4573            c_tag_scope_witness: false,
4574            recovered_class_sibling_scopes: HashMap::default(),
4575            consumed_fragment_regions: Vec::new(),
4576            orphaned_namespaces: &orphaned_namespaces,
4577            partitioned_regions: Vec::new(),
4578            namespace_forward_scans: HashMap::default(),
4579            field_owners: None,
4580            recovery_captures: Vec::new(),
4581            active_source_context: None,
4582            object_macro_fields: object_macro_fields.clone(),
4583            ambiguous_object_macro_fields: HashSet::default(),
4584        })
4585        .collect();
4586    let mut declaration_stacks: Vec<_> = dialects
4587        .iter()
4588        .map(|_| {
4589            vec![CppWork::Container(CppContainer {
4590                node: root,
4591                scope: ScopeInfo {
4592                    package_name: String::new(),
4593                    module: None,
4594                    class_unit: None,
4595                    template_signature: None,
4596                    template_metadata: None,
4597                    declarations_are_fields: false,
4598                    recovered_specialization_member_scope: false,
4599                    visible_using_namespaces: Vec::new(),
4600                },
4601            })]
4602        })
4603        .collect();
4604    let kinds = CompiledKinds::compile(
4605        &tree_sitter_cpp::LANGUAGE.into(),
4606        CPP_STRUCTURAL_SPEC.kind_table(),
4607    );
4608    // Forward macro names affect calls before their definitions and require the
4609    // existing bounded preprocessor-only context prepass.
4610    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4611    let mut structural = StructuralFactCollector::new(
4612        &CPP_STRUCTURAL_SPEC,
4613        source,
4614        &context,
4615        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4616        usize::MAX,
4617        None,
4618    );
4619    let mut imports = Vec::new();
4620    let mut includes = Vec::new();
4621    let mut expressions = Vec::new();
4622    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4623    let mut statement_facts = CppStatementFactRows::default();
4624    let mut identifiers = HashSet::default();
4625    let mut entered = HashSet::default();
4626    let mut primary_ordinals = HashMap::default();
4627    let mut stack = vec![(root, None)];
4628    while let Some((node, parent)) = stack.pop() {
4629        assert!(entered.insert(node.id()), "one primary event per AST node");
4630        primary_ordinals.insert(node.id(), primary_ordinals.len());
4631        dependencies.borrow_mut().primary.enter(node, source);
4632        let mut child_parent = parent;
4633        if node.is_named() {
4634            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4635            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4636                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4637            }
4638            if let Some(raw_kind) = kinds.kind_of(&node)
4639                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4640            {
4641                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4642                    node,
4643                    raw_kind,
4644                    parent.map(|id| structural.normalized_kind(id)),
4645                    source,
4646                    &context,
4647                );
4648                let mut source_facts = collector.borrow_mut();
4649                let id = structural
4650                    .enter(node, kind, parent, &mut source_facts)
4651                    .expect("unbounded C++ structural collector");
4652                let mut sink = structural.role_sink(&mut source_facts);
4653                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4654                structural
4655                    .accept_roles(id, sink.into_parts())
4656                    .expect("unbounded C++ structural roles");
4657                child_parent = Some(id);
4658            }
4659            if let Some(fact) = crate::source_facts::capture_expression_fact(
4660                node,
4661                source,
4662                collector.borrow_mut().intern_node(node),
4663                &named_record_sizes,
4664            ) {
4665                expressions.push(fact);
4666            }
4667            if node.kind() == "preproc_include" {
4668                let raw = normalize_cpp_whitespace(node_text(node, source));
4669                if !raw.is_empty() {
4670                    let occurrence = collector.borrow_mut().intern_node(node);
4671                    let target = node.child_by_field_name("path").and_then(|path| {
4672                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4673                        let target = collector.borrow_mut().intern_node(path);
4674                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4675                            declaration: occurrence,
4676                            target,
4677                            path: value.to_owned(),
4678                            quoted,
4679                        });
4680                        Some(target)
4681                    });
4682                    imports.push(SourceImportFact::from_import(
4683                        cpp_include_info(raw),
4684                        occurrence,
4685                        target,
4686                        None,
4687                        Vec::new(),
4688                    ));
4689                }
4690            }
4691            if matches!(
4692                node.kind(),
4693                "type_identifier" | "identifier" | "qualified_identifier"
4694            ) {
4695                let text = node_text(node, source).trim();
4696                if !text.is_empty() {
4697                    identifiers.insert(text.to_owned());
4698                }
4699            }
4700        }
4701        for index in 0..visitors.len() {
4702            // Delay the alternative policy until the primary walk encounters a
4703            // tag whose scope can differ. Its saved declaration stack catches up
4704            // through already-entered events, including recovery, without a
4705            // second AST traversal. A false witness performs no C declaration work.
4706            if index != 0 && !visitors[0].c_tag_scope_witness {
4707                continue;
4708            }
4709            let visitor = &mut visitors[index];
4710            advance_primary_declarations(
4711                visitor,
4712                &mut declaration_stacks[index],
4713                &entered,
4714                ancestry,
4715            );
4716            visitor.drain_partitioned_regions();
4717        }
4718        let mut cursor = node.walk();
4719        let children: Vec<_> = node
4720            .children(&mut cursor)
4721            .map(|child| (child, child_parent))
4722            .collect();
4723        stack.extend(children.into_iter().rev());
4724    }
4725    let reading_count = if visitors[0].c_tag_scope_witness {
4726        visitors.len()
4727    } else {
4728        1
4729    };
4730    visitors.truncate(reading_count);
4731    declaration_stacks.truncate(reading_count);
4732    assert!(
4733        declaration_stacks.iter().all(Vec::is_empty),
4734        "all primary declaration events were consumed"
4735    );
4736    for visitor in &mut visitors {
4737        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4738            .into_iter()
4739            .flat_map(|(unit, declarations)| {
4740                declarations
4741                    .into_iter()
4742                    .map(move |declaration| (declaration, unit.clone()))
4743            })
4744            .collect();
4745    }
4746    let dialect_declarations: Vec<_> = visitors
4747        .iter()
4748        .map(|visitor| visitor.source_declarations.clone())
4749        .collect();
4750    drop(visitors);
4751    readings.truncate(reading_count);
4752    dependencies
4753        .borrow_mut()
4754        .finish_primary(&mut properties.borrow_mut());
4755    // Preserve the existing malformed quoted-directive recovery, but publish its
4756    // explicit source occurrence into the same import family as parsed directives.
4757    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4758    let primary_occurrence_ordinals: HashMap<_, _> = collector
4759        .borrow()
4760        .primary_node_occurrences()
4761        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4762        .collect();
4763    let occurrences = collector.into_inner().finish();
4764    let generic_imports: Vec<_> = (0..imports.len())
4765        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4766        .collect();
4767    let source_facts = ParsedSourceFacts {
4768        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4769            includes,
4770            declarations: {
4771                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4772                facts.sort_by_key(|fact| fact.declaration.get());
4773                facts
4774            },
4775            using_namespaces,
4776            expressions,
4777            statement_facts,
4778        }),
4779        go: None,
4780        java: None,
4781        js_ts: None,
4782        php: None,
4783        scala: None,
4784        ruby: None,
4785        python: None,
4786        source_bytes: source.len(),
4787        occurrences,
4788        structural: structural
4789            .finish(root)
4790            .expect("C++ structural collection finishes"),
4791        native_site_occurrences: Vec::new(),
4792        native_declaration_sources: Vec::new(),
4793        declaration_visibilities: None,
4794        rust_declaration_properties: Vec::new(),
4795        rust_modules: None,
4796        rust_types: Vec::new(),
4797        rust_items: Default::default(),
4798        imports,
4799        generic_imports,
4800        rust_import_contexts: Vec::new(),
4801    };
4802    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4803        parsed.finalize_deferred_replacements();
4804        let links = std::mem::take(&mut parsed.source_declaration_units);
4805        parsed.source_declaration_units = links
4806            .into_iter()
4807            .filter(|(_, unit)| parsed.contains_declaration(unit))
4808            .collect();
4809        parsed.imports = source_facts
4810            .imports
4811            .iter()
4812            .map(|import| import.import_info(&source_facts.occurrences))
4813            .collect();
4814        parsed.type_identifiers = identifiers.clone();
4815        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4816            &source_facts,
4817            &declarations,
4818            &primary_occurrence_ordinals,
4819            &mut parsed.source_declaration_units,
4820            &mut parsed.source_declaration_metadata,
4821        ));
4822    }
4823    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4824        // The recovery runs to the end of the file, which is how a reader of
4825        // the record tells this recovery from a local one (#3501).
4826        let recovery = Range {
4827            start_byte: outermost.start_byte(),
4828            end_byte: source.len(),
4829            start_line: outermost.start_position().row + 1,
4830            end_line: root.end_position().row + 1,
4831        };
4832        for parsed in &mut readings {
4833            record_declarations_left_open(parsed, recovery);
4834        }
4835    }
4836    readings
4837}
4838
4839fn advance_primary_declarations<'tree>(
4840    visitor: &mut CppVisitor<'_, '_>,
4841    stack: &mut Vec<CppWork<'tree>>,
4842    entered: &HashSet<usize>,
4843    ancestry: &ParentIndex<'tree>,
4844) {
4845    while let Some(work) = stack.pop() {
4846        match work {
4847            CppWork::Container(container) => {
4848                push_cpp_container_work(container.node, container.scope, stack)
4849            }
4850            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4851            CppWork::Node(work) => {
4852                if !entered.contains(&work.node.id()) {
4853                    stack.push(CppWork::Node(work));
4854                    return;
4855                }
4856                if !visitor.node_is_inside_consumed_fragment(work.node) {
4857                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4858                }
4859            }
4860        }
4861    }
4862}
4863
4864#[derive(Clone)]
4865struct CppActiveSourceContext {
4866    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4867    namespace: Option<String>,
4868    child_namespace: Option<String>,
4869    outer_lexical_scope: Vec<String>,
4870    child_lexical_scope: Vec<String>,
4871    family: Option<SourceOccurrenceId>,
4872    exhaustive_family: Option<SourceOccurrenceId>,
4873    callable_guard_completion_byte: Option<usize>,
4874}
4875
4876/// One owned container reparse deferred until the primary declaration walk has
4877/// reached its source position. The tree and context are owned so the deferred
4878/// walk never retains a `Node` borrowed from a temporary recovery tree.
4879pub struct CppPartitionedRegion {
4880    tree: Tree,
4881    range: std::ops::Range<usize>,
4882    scope: ScopeInfo,
4883    recovery: Range,
4884    source_context: Option<CppActiveSourceContext>,
4885}
4886
4887pub struct CppVisitor<'a, 'source> {
4888    pub file: &'a ProjectFile,
4889    pub source: &'source str,
4890    pub parsed: &'a mut ParsedFile,
4891    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4892    source_properties: &'a RefCell<
4893        HashMap<
4894            SourceDeclarationId,
4895            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4896        >,
4897    >,
4898    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4899    pub primary_ancestry: &'a ParentIndex<'a>,
4900    pub primary_root_id: usize,
4901    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4902    source_declarations: HashSet<SourceDeclarationId>,
4903    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
4904    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
4905    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
4906    ///
4907    /// C has no nested tag scope: a struct/union/enum tag declared inside
4908    /// another aggregate's member list has the scope of the outer declaration
4909    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
4910    /// therefore declares a file-scope `inner` that a later file-scope
4911    /// `struct inner *p;` legitimately references, where C++ would make the
4912    /// same shape a nested class `outer::inner`. Headers carry no compilation
4913    /// language of their own and keep the conservative C++ interpretation.
4914    pub c_tag_semantics: bool,
4915    /// Sticky evidence that this C++ walk reached a declaration whose scope
4916    /// would change under C tag semantics, including visitor recovery work.
4917    pub(crate) c_tag_scope_witness: bool,
4918    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
4919    /// Byte regions whose contents were re-owned by a fragmented export-class
4920    /// recovery (#938): the scattered members between the fragmented
4921    /// declaration and its displaced closing brace are indexed as members of
4922    /// the recovered class by the region reparse, so the ordinary sibling walk
4923    /// must not ALSO index them as top-level declarations (that double-indexing
4924    /// made a scattered nested class ambiguous between `Inner` and
4925    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
4926    /// linear scan at visit time is fine.
4927    pub consumed_fragment_regions: Vec<(usize, usize)>,
4928    /// Lexical namespace corrections and brace boundaries for declarations
4929    /// displaced by parse recovery (issues #1537 and #3087). The parsed
4930    /// ancestors can stop short or extend past their true closes; see
4931    /// [`CppVisitor::recovered_namespace_scope`].
4932    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
4933    /// Owned reparses waiting for the outer work loop. Partitioning another
4934    /// swallowed class in a tail must not grow the Rust call stack.
4935    pub partitioned_regions: Vec<CppPartitionedRegion>,
4936    /// The namespace forward declarations already folded out of each tree this
4937    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
4938    /// Empty until the first question, which the overwhelming majority of files
4939    /// never ask.
4940    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
4941    /// Which owners already have field declarations in the parse product, as
4942    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
4943    /// the first enum asks, which most files never do (#2786).
4944    pub field_owners: Option<CppFieldOwnerIndex>,
4945    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
4946    /// happen to the declaration set, innermost last. Empty outside a recovery
4947    /// reparse, which is almost always (#2787).
4948    pub recovery_captures: Vec<CppRecoveryCapture>,
4949    active_source_context: Option<CppActiveSourceContext>,
4950    /// Object-like field-list macros defined earlier in this source. Their
4951    /// replacements are parsed structurally and materialized under each
4952    /// invoking aggregate; no source-text expansion is used.
4953    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4954    /// Names whose active replacement is not a unique structured field list.
4955    /// A later `#undef` resets the ambiguity; another `#define` does not.
4956    pub ambiguous_object_macro_fields: HashSet<String>,
4957}
4958
4959/// One source-order event from an object-like field-list macro environment.
4960///
4961/// The event stream lets an include-closure consumer preserve preprocessor
4962/// invalidation without guessing which parsed header happened to be visited
4963/// first. A conservative consumer may permanently block a name after an
4964/// `undef` when conditional include order is not provable.
4965#[derive(Clone, Debug, PartialEq, Eq)]
4966pub enum ObjectMacroFieldEvent {
4967    Define {
4968        name: String,
4969        replacement: ObjectMacroReplacement,
4970        conditional: bool,
4971    },
4972    Undef {
4973        name: String,
4974        conditional: bool,
4975    },
4976}
4977
4978/// Collect object-like field-list macros from a parsed source in source order.
4979/// Macros with conflicting active replacements are omitted so an include
4980/// closure cannot silently choose one guarded definition over another.
4981pub fn collect_cpp_object_macro_fields<'tree>(
4982    root: Node<'tree>,
4983    source: &str,
4984) -> HashMap<String, ObjectMacroReplacement> {
4985    let mut fields = HashMap::default();
4986    let mut ambiguous = HashSet::default();
4987    for event in collect_cpp_object_macro_field_events(root, source) {
4988        match event {
4989            ObjectMacroFieldEvent::Define {
4990                name,
4991                replacement: value,
4992                conditional,
4993            } => {
4994                if value.is_empty() {
4995                    fields.remove(&name);
4996                    if conditional {
4997                        ambiguous.insert(name);
4998                    } else {
4999                        ambiguous.remove(&name);
5000                    }
5001                } else if ambiguous.contains(&name) {
5002                    // Keep the name blocked until an explicit undef resets it.
5003                } else if let Some(previous) = fields.get(&name) {
5004                    if previous != &value {
5005                        fields.remove(&name);
5006                        ambiguous.insert(name);
5007                    }
5008                } else {
5009                    fields.insert(name, value);
5010                }
5011            }
5012            ObjectMacroFieldEvent::Undef { name, conditional } => {
5013                fields.remove(&name);
5014                if conditional {
5015                    ambiguous.insert(name);
5016                } else {
5017                    ambiguous.remove(&name);
5018                }
5019            }
5020        }
5021    }
5022    fields
5023}
5024
5025/// Collect object-like field-list macro events in source order. Conditional
5026/// branches remain visible in the event stream: callers that cannot prove
5027/// branch selection can invalidate names conservatively instead of inventing
5028/// a replacement from one branch.
5029pub fn collect_cpp_object_macro_field_events<'tree>(
5030    root: Node<'tree>,
5031    source: &str,
5032) -> Vec<ObjectMacroFieldEvent> {
5033    let mut events = Vec::new();
5034    let mut stack = vec![root];
5035    while let Some(node) = stack.pop() {
5036        if node.kind() == "preproc_def"
5037            && let Some(name) = extract_macro_name(node, source)
5038        {
5039            let replacement = object_macro_replacement_of(node, source);
5040            events.push(ObjectMacroFieldEvent::Define {
5041                name,
5042                replacement,
5043                conditional: inside_preprocessor_conditional(node),
5044            });
5045        } else if is_cpp_undef_directive(node, source)
5046            && let Some(argument) = node.child_by_field_name("argument")
5047        {
5048            events.push(ObjectMacroFieldEvent::Undef {
5049                name: node_text(argument, source).trim().to_string(),
5050                conditional: inside_preprocessor_conditional(node),
5051            });
5052        }
5053        let mut cursor = node.walk();
5054        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5055        stack.extend(children.into_iter().rev());
5056    }
5057    events
5058}
5059
5060fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5061    let mut current = node.parent();
5062    while let Some(parent) = current {
5063        if is_cpp_preprocessor_conditional(parent) {
5064            return true;
5065        }
5066        current = parent.parent();
5067    }
5068    false
5069}
5070
5071fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5072    node.kind() == "preproc_call"
5073        && node
5074            .child_by_field_name("directive")
5075            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5076}
5077
5078impl<'a, 'source> CppVisitor<'a, 'source> {
5079    fn apply_active_source_context(
5080        &self,
5081        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5082    ) {
5083        let Some(context) = self.active_source_context.as_ref() else {
5084            return;
5085        };
5086        fact.guard_requirements = fact
5087            .guard_requirements
5088            .as_ref()
5089            .and_then(crate::source_context::cpp_guard_set_to_runtime)
5090            .zip(context.guards.as_ref())
5091            .and_then(|(local, outer)| {
5092                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5093            })
5094            .as_ref()
5095            .map(crate::source_context::cpp_guard_set_from_runtime);
5096        if fact.callable_guards.is_some() {
5097            fact.callable_guards = fact
5098                .callable_guards
5099                .as_ref()
5100                .and_then(crate::source_context::cpp_guard_set_to_runtime)
5101                .zip(context.guards.as_ref())
5102                .and_then(|(local, outer)| {
5103                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5104                })
5105                .as_ref()
5106                .map(crate::source_context::cpp_guard_set_from_runtime);
5107        }
5108        fact.callable_guard_completion_byte = fact
5109            .callable_guard_completion_byte
5110            .zip(context.callable_guard_completion_byte)
5111            .map(|(local, outer)| local.max(outer));
5112        fact.conditional_family = fact.conditional_family.or(context.family);
5113        fact.exhaustive_conditional_family = fact
5114            .exhaustive_conditional_family
5115            .or(context.exhaustive_family);
5116        if let Some(alias) = fact.file_scope_alias.as_mut() {
5117            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5118                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5119                (Some(outer), None) => Some(outer.clone()),
5120                (None, local) => local,
5121            };
5122        }
5123        if !context.outer_lexical_scope.is_empty() {
5124            let mut lexical = context.outer_lexical_scope.clone();
5125            lexical.append(&mut fact.lexical_path);
5126            fact.lexical_path = lexical;
5127        }
5128    }
5129
5130    fn recovery_ancestry<'tree>(
5131        &self,
5132        root: Node<'tree>,
5133    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5134        crate::source_dependencies::RecoveryAncestry::new(
5135            root,
5136            self.source,
5137            self.source_dependencies,
5138        )
5139    }
5140
5141    fn capture_source_dependencies(
5142        &self,
5143        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5144        node: Node<'_>,
5145        unit: &CodeUnit,
5146    ) {
5147        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5148            self.source_dependencies.borrow_mut().register_primary(
5149                fact.declaration,
5150                node,
5151                unit.is_class(),
5152            );
5153        } else {
5154            self.source_dependencies
5155                .borrow()
5156                .apply_recovered(node, unit.is_class(), fact);
5157        }
5158    }
5159
5160    fn link_source_declaration(
5161        &mut self,
5162        ancestry: &ParentIndex<'_>,
5163        unit: &CodeUnit,
5164        node: Node<'_>,
5165    ) {
5166        let (declaration, family) = {
5167            let mut collector = self.source_collector.borrow_mut();
5168            let occurrence = if node.id() == self.primary_root_id
5169                || self.primary_ancestry.contains(node)
5170            {
5171                collector.intern_node(node)
5172            } else {
5173                // A region reparse owns distinct provenance even when its byte range
5174                // equals a primary node or a previously dropped recovery tree.
5175                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5176            };
5177            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5178                if self.primary_ancestry.contains(name) {
5179                    collector.intern_node(name)
5180                } else {
5181                    collector
5182                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5183                }
5184            });
5185            let declaration = collector.declare(occurrence, name);
5186            let family =
5187                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5188                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5189                    {
5190                        collector.intern_node(family)
5191                    } else {
5192                        collector.intern_embedded(
5193                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5194                        )
5195                    }
5196                });
5197            (declaration, family)
5198        };
5199        self.source_declarations.insert(declaration);
5200        if !self.source_properties.borrow().contains_key(&declaration) {
5201            let context = crate::source_context::capture_declaration_context(
5202                node,
5203                unit,
5204                self.source,
5205                &mut self.preprocessor_context_cache,
5206            );
5207            let (exhaustive_family, displaced_namespace_closing_brace) = {
5208                let mut collector = self.source_collector.borrow_mut();
5209                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5210                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5211                    {
5212                        collector.intern_node(family)
5213                    } else {
5214                        collector.intern_embedded(
5215                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5216                        )
5217                    }
5218                });
5219                let displaced_namespace_closing_brace =
5220                    context.displaced_namespace_closing_brace.map(|brace| {
5221                        if brace.id() == self.primary_root_id
5222                            || self.primary_ancestry.contains(brace)
5223                        {
5224                            collector.intern_node(brace)
5225                        } else {
5226                            collector.intern_embedded(
5227                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5228                            )
5229                        }
5230                    });
5231                (exhaustive_family, displaced_namespace_closing_brace)
5232            };
5233            let guard_requirements = context.guard_requirements;
5234            let callable_guards = context.callable_guards;
5235            let callable_activation = context.callable_activation;
5236            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5237            let flattened_macro_namespace = context.flattened_macro_namespace;
5238            self.source_properties
5239                .borrow_mut()
5240                .entry(declaration)
5241                .or_insert_with(|| {
5242                    let mut fact = crate::source_facts::capture_declaration(
5243                        declaration,
5244                        node,
5245                        unit,
5246                        self.source,
5247                        ancestry,
5248                    );
5249                    self.capture_source_dependencies(&mut fact, node, unit);
5250                    fact.occurrence_role =
5251                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5252                    fact.conditional_family = family;
5253                    fact.guard_requirements = guard_requirements;
5254                    fact.callable_guards = callable_guards;
5255                    fact.callable_activation = callable_activation;
5256                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
5257                    fact.exhaustive_conditional_family = exhaustive_family;
5258                    fact.flattened_macro_namespace = flattened_macro_namespace;
5259                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5260                    self.apply_active_source_context(&mut fact);
5261                    fact
5262                });
5263        }
5264        let links = self.source_links.entry(unit.clone()).or_default();
5265        if !links.contains(&declaration) {
5266            links.push(declaration);
5267        }
5268    }
5269
5270    fn link_source_range(
5271        &mut self,
5272        ancestry: &ParentIndex<'_>,
5273        unit: &CodeUnit,
5274        range: Range,
5275        evidence: Option<Node<'_>>,
5276    ) {
5277        let declaration = {
5278            let mut collector = self.source_collector.borrow_mut();
5279            let occurrence =
5280                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5281            let name = evidence
5282                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5283                .map(|name| {
5284                    if self.primary_ancestry.contains(name) {
5285                        collector.intern_node(name)
5286                    } else {
5287                        collector.intern_embedded(
5288                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5289                        )
5290                    }
5291                });
5292            collector.declare(occurrence, name)
5293        };
5294        self.link_source_declaration_facts(
5295            unit,
5296            declaration,
5297            evidence.map(|node| (ancestry, node)),
5298        );
5299    }
5300
5301    /// [`Self::link_source_range`] with the name occurrence interned from the
5302    /// subspan that writes it instead of derived from an evidence node. A live
5303    /// evidence node still supplies the declaration's source facts.
5304    fn link_source_name_range(
5305        &mut self,
5306        unit: &CodeUnit,
5307        range: Range,
5308        name_range: Range,
5309        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5310    ) {
5311        let declaration = {
5312            let mut collector = self.source_collector.borrow_mut();
5313            let occurrence =
5314                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5315            let name =
5316                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5317            collector.declare(occurrence, Some(name))
5318        };
5319        self.link_source_declaration_facts(unit, declaration, evidence);
5320    }
5321
5322    /// Record `declaration` for `unit` and, when a live evidence node exists,
5323    /// the source facts it supplies: conditional family, guards, context and
5324    /// dependency names. Shared by both occurrence-naming paths so a name
5325    /// subspan never costs a declaration its facts (#3093).
5326    fn link_source_declaration_facts(
5327        &mut self,
5328        unit: &CodeUnit,
5329        declaration: SourceDeclarationId,
5330        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5331    ) {
5332        let family = evidence.and_then(|(_, node)| {
5333            let mut collector = self.source_collector.borrow_mut();
5334            crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5335                if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5336                    collector.intern_node(family)
5337                } else {
5338                    collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5339                        family,
5340                    ))
5341                }
5342            })
5343        });
5344        self.source_declarations.insert(declaration);
5345        if let Some((ancestry, node)) = evidence
5346            && !self.source_properties.borrow().contains_key(&declaration)
5347        {
5348            let context = crate::source_context::capture_declaration_context(
5349                node,
5350                unit,
5351                self.source,
5352                &mut self.preprocessor_context_cache,
5353            );
5354            let (exhaustive_family, displaced_namespace_closing_brace) = {
5355                let mut collector = self.source_collector.borrow_mut();
5356                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5357                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5358                    {
5359                        collector.intern_node(family)
5360                    } else {
5361                        collector.intern_embedded(
5362                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5363                        )
5364                    }
5365                });
5366                let displaced_namespace_closing_brace =
5367                    context.displaced_namespace_closing_brace.map(|brace| {
5368                        if brace.id() == self.primary_root_id
5369                            || self.primary_ancestry.contains(brace)
5370                        {
5371                            collector.intern_node(brace)
5372                        } else {
5373                            collector.intern_embedded(
5374                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5375                            )
5376                        }
5377                    });
5378                (exhaustive_family, displaced_namespace_closing_brace)
5379            };
5380            let guard_requirements = context.guard_requirements;
5381            let callable_guards = context.callable_guards;
5382            let callable_activation = context.callable_activation;
5383            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5384            let flattened_macro_namespace = context.flattened_macro_namespace;
5385            let mut fact = crate::source_facts::capture_declaration(
5386                declaration,
5387                node,
5388                unit,
5389                self.source,
5390                ancestry,
5391            );
5392            self.capture_source_dependencies(&mut fact, node, unit);
5393            fact.occurrence_role =
5394                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5395            fact.conditional_family = family;
5396            fact.guard_requirements = guard_requirements;
5397            fact.callable_guards = callable_guards;
5398            fact.callable_activation = callable_activation;
5399            fact.callable_guard_completion_byte = callable_guard_completion_byte;
5400            fact.exhaustive_conditional_family = exhaustive_family;
5401            fact.flattened_macro_namespace = flattened_macro_namespace;
5402            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5403            self.apply_active_source_context(&mut fact);
5404            self.source_properties
5405                .borrow_mut()
5406                .insert(declaration, fact);
5407        }
5408        let links = self.source_links.entry(unit.clone()).or_default();
5409        if !links.contains(&declaration) {
5410            links.push(declaration);
5411        }
5412    }
5413
5414    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5415        let declaration = *self
5416            .source_links
5417            .get(&unit)
5418            .and_then(|links| links.last())
5419            .expect("signature metadata follows its source declaration");
5420        let metadata_ordinal = self
5421            .parsed
5422            .add_signature_with_metadata(unit.clone(), metadata);
5423        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5424            declaration,
5425            unit,
5426            metadata_ordinal,
5427        };
5428        if !self.parsed.source_declaration_metadata.contains(&link) {
5429            self.parsed.source_declaration_metadata.push(link);
5430        }
5431    }
5432
5433    /// Records `code_unit` with the answers the walk carries forward, then adds
5434    /// it to the parse product.
5435    ///
5436    /// Every declaration this walk publishes goes through this family, so the
5437    /// carried-forward answers see each one exactly once: the field ownership
5438    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
5439    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5440    fn add_declaration(
5441        &mut self,
5442        ancestry: &ParentIndex<'_>,
5443        code_unit: CodeUnit,
5444        node: Node<'_>,
5445        parent: Option<CodeUnit>,
5446        top_level: Option<CodeUnit>,
5447    ) {
5448        self.note_declaration(&code_unit);
5449        self.link_source_declaration(ancestry, &code_unit, node);
5450        let source = self.source;
5451        self.parsed
5452            .add_code_unit(code_unit, node, source, parent, top_level);
5453    }
5454
5455    /// Range-based form of [`Self::add_declaration`].
5456    fn add_declaration_with_range(
5457        &mut self,
5458        ancestry: &ParentIndex<'_>,
5459        code_unit: CodeUnit,
5460        range: Range,
5461        evidence: Option<Node<'_>>,
5462        parent: Option<CodeUnit>,
5463        top_level: Option<CodeUnit>,
5464    ) {
5465        self.note_declaration(&code_unit);
5466        self.link_source_range(ancestry, &code_unit, range, evidence);
5467        self.parsed
5468            .add_code_unit_with_range(code_unit, range, parent, top_level);
5469    }
5470
5471    /// Name-subspan form of [`Self::add_declaration_with_range`]: the name
5472    /// occurrence is the recorded subspan rather than a token derived from an
5473    /// evidence node (#4292). A live `evidence` node, when the caller has one,
5474    /// still supplies the declaration's source facts; an index-recovered
5475    /// declaration has none.
5476    fn add_declaration_with_name_range(
5477        &mut self,
5478        code_unit: CodeUnit,
5479        range: Range,
5480        name_range: Range,
5481        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5482        parent: Option<CodeUnit>,
5483        top_level: Option<CodeUnit>,
5484    ) {
5485        self.note_declaration(&code_unit);
5486        self.link_source_name_range(&code_unit, range, name_range, evidence);
5487        self.parsed
5488            .add_code_unit_with_range(code_unit, range, parent, top_level);
5489    }
5490
5491    /// Deferred-replacement form of [`Self::add_declaration`].
5492    fn replace_declaration_deferred(
5493        &mut self,
5494        ancestry: &ParentIndex<'_>,
5495        code_unit: CodeUnit,
5496        node: Node<'_>,
5497        parent: Option<CodeUnit>,
5498        top_level: Option<CodeUnit>,
5499    ) {
5500        self.note_replaced_declaration(&code_unit);
5501        self.link_source_declaration(ancestry, &code_unit, node);
5502        let source = self.source;
5503        self.parsed
5504            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5505    }
5506
5507    /// Range-based form of [`Self::replace_declaration_deferred`].
5508    fn replace_declaration_with_range_deferred(
5509        &mut self,
5510        ancestry: &ParentIndex<'_>,
5511        code_unit: CodeUnit,
5512        range: Range,
5513        evidence: Option<Node<'_>>,
5514        parent: Option<CodeUnit>,
5515        top_level: Option<CodeUnit>,
5516    ) {
5517        self.note_replaced_declaration(&code_unit);
5518        self.link_source_range(ancestry, &code_unit, range, evidence);
5519        self.parsed
5520            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5521    }
5522
5523    /// Notes one declaration about to enter the parse product.
5524    ///
5525    /// A declaration the product already holds is not a creation, so an open
5526    /// recovery capture ignores it -- which is the membership test the set
5527    /// difference it replaces performed. A creation inside a nested recovery
5528    /// belongs to the recoveries around it too, so every open capture takes it.
5529    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5530        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5531            for capture in &mut self.recovery_captures {
5532                if capture.removed_pre_existing.contains(code_unit) {
5533                    continue;
5534                }
5535                if capture.created_units.insert(code_unit.clone()) {
5536                    capture.created.push(code_unit.clone());
5537                }
5538            }
5539        }
5540        if let Some(field_owners) = self.field_owners.as_mut() {
5541            field_owners.record(code_unit, self.file);
5542        }
5543    }
5544
5545    /// Notes one declaration about to replace an existing one.
5546    ///
5547    /// A deferred replacement of a declaration that already owns children
5548    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5549    /// a removal is the one thing the field index cannot absorb by addition.
5550    /// Drop it; the next question rebuilds it from the declarations that
5551    /// survive. A replacement of a unit with no children, and a "replacement"
5552    /// of a unit that is not there at all, remove nothing.
5553    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5554        // Forward and definition occurrences remain real navigation alternatives.
5555        // Only identities removed with replaced children lose their mounts.
5556        for removed in self.declarations_a_replacement_removes(code_unit) {
5557            self.source_links.remove(&removed);
5558        }
5559        let removes_children = self.parsed.contains_declaration(code_unit)
5560            && self
5561                .parsed
5562                .children
5563                .get(code_unit)
5564                .is_some_and(|children| !children.is_empty());
5565        if removes_children {
5566            if !self.recovery_captures.is_empty() {
5567                let removed = self.declarations_a_replacement_removes(code_unit);
5568                for capture in &mut self.recovery_captures {
5569                    for unit in &removed {
5570                        // A declaration this capture watched being created is
5571                        // its own; one it did not is a declaration that was
5572                        // already there when the capture opened, so creating it
5573                        // again is a restoration and not a mint.
5574                        if !capture.created_units.contains(unit) {
5575                            capture.removed_pre_existing.insert(unit.clone());
5576                        }
5577                    }
5578                }
5579            }
5580            self.field_owners = None;
5581        }
5582        self.note_declaration(code_unit);
5583    }
5584
5585    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5586    /// when `code_unit` is replaced: its children, transitively.
5587    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5588        let mut removed = Vec::new();
5589        let mut seen = HashSet::default();
5590        let mut pending: Vec<CodeUnit> = self
5591            .parsed
5592            .children
5593            .get(code_unit)
5594            .cloned()
5595            .unwrap_or_default();
5596        while let Some(unit) = pending.pop() {
5597            if !seen.insert(unit.clone()) {
5598                continue;
5599            }
5600            if let Some(children) = self.parsed.children.get(&unit) {
5601                pending.extend(children.iter().cloned());
5602            }
5603            removed.push(unit);
5604        }
5605        removed
5606    }
5607
5608    fn visit_recovered_function_like_export_class<'tree>(
5609        &mut self,
5610        node: Node<'tree>,
5611        recovered: RecoveredFunctionLikeExportClass,
5612        scope: &ScopeInfo,
5613        stack: &mut Vec<CppWork<'tree>>,
5614        ancestry: &ParentIndex<'tree>,
5615    ) {
5616        let RecoveredFunctionLikeExportClass {
5617            name,
5618            range,
5619            raw_supertypes,
5620            base_facts,
5621            member_outcome,
5622        } = recovered;
5623        let class_unit = self.visit_named_class_like_shape(
5624            node,
5625            name,
5626            None,
5627            true,
5628            Some(range),
5629            raw_supertypes,
5630            base_facts,
5631            scope,
5632            stack,
5633            ancestry,
5634        );
5635        self.parsed
5636            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5637                recovery: range,
5638                unit: class_unit.clone(),
5639            });
5640        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5641            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5642                tree.root_node(),
5643                class_unit.identifier(),
5644                self.source,
5645            )
5646        {
5647            self.visit_recovered_fragment_constructor(
5648                constructor_range,
5649                body,
5650                node,
5651                &class_unit,
5652                scope,
5653                ancestry,
5654            );
5655        }
5656        if let Some(outcome) = member_outcome {
5657            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5658        }
5659    }
5660
5661    fn visit_function_like_export_class_pair<'tree>(
5662        &mut self,
5663        node: Node<'tree>,
5664        scope: &ScopeInfo,
5665        stack: &mut Vec<CppWork<'tree>>,
5666        ancestry: &ParentIndex<'tree>,
5667    ) -> bool {
5668        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5669            return false;
5670        };
5671        let member_outcome = self
5672            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5673        // A malformed class body can escape into several following siblings
5674        // before the next export-macro class head appears. Inspect siblings in
5675        // order and stop at the first envelope that contains such a head. One
5676        // envelope can contain several following classes, all recovered in a
5677        // single bounded traversal.
5678        let mut displaced = node.next_named_sibling();
5679        while let Some(candidate) = displaced {
5680            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5681                break;
5682            }
5683            displaced = candidate.next_named_sibling();
5684        }
5685        // The adjacent initializer_list proves the class body envelope, but
5686        // its children are expression-shaped rather than declaration-shaped.
5687        // Visit only separately recovered members; ordinary callable
5688        // definitions remain available from their out-of-line declarations.
5689        let range = recovered.range;
5690        self.visit_recovered_function_like_export_class(
5691            node,
5692            RecoveredFunctionLikeExportClass {
5693                name: recovered.name,
5694                range,
5695                raw_supertypes: recovered.raw_supertypes,
5696                base_facts: recovered.base_facts,
5697                member_outcome,
5698            },
5699            scope,
5700            stack,
5701            ancestry,
5702        );
5703        self.consumed_fragment_regions
5704            .push((node.start_byte(), range.end_byte));
5705        true
5706    }
5707
5708    fn visit_embedded_function_like_export_classes<'tree>(
5709        &mut self,
5710        node: Node<'tree>,
5711        scope: &ScopeInfo,
5712        stack: &mut Vec<CppWork<'tree>>,
5713        ancestry: &ParentIndex<'tree>,
5714    ) -> bool {
5715        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5716        let found = !recovered_classes.is_empty();
5717        for recovered in recovered_classes {
5718            let member_outcome = self.reparse_fragmented_export_class_members(
5719                &recovered.fragmented_body,
5720                &recovered.name,
5721            );
5722            self.visit_recovered_function_like_export_class(
5723                node,
5724                RecoveredFunctionLikeExportClass {
5725                    name: recovered.name,
5726                    range: recovered.range,
5727                    raw_supertypes: Some(recovered.raw_supertypes),
5728                    base_facts: Some(recovered.base_facts),
5729                    member_outcome,
5730                },
5731                scope,
5732                stack,
5733                ancestry,
5734            );
5735        }
5736        found
5737    }
5738
5739    /// Walk `node`'s container, answering every ancestor question from
5740    /// `ancestry`.
5741    ///
5742    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5743    /// because one tree can be walked more than once -- a header's C and C++
5744    /// readings are the same tree under different tag semantics -- and the
5745    /// parent relation is a property of the tree, not of the reading.
5746    #[allow(clippy::too_many_arguments)]
5747    pub fn visit_container<'tree>(
5748        &mut self,
5749        node: Node<'tree>,
5750        ancestry: &ParentIndex<'tree>,
5751        package_name: &str,
5752        module: Option<CodeUnit>,
5753        class_unit: Option<CodeUnit>,
5754        template_signature: Option<String>,
5755        visible_using_namespaces: Vec<String>,
5756    ) {
5757        let scope = ScopeInfo {
5758            package_name: package_name.to_string(),
5759            module,
5760            class_unit,
5761            template_signature,
5762            template_metadata: None,
5763            declarations_are_fields: false,
5764            recovered_specialization_member_scope: false,
5765            visible_using_namespaces,
5766        };
5767        // The work loop dispatches a container's children, never the container
5768        // itself, so an `ERROR` container that is the seed does not reach the
5769        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5770        // breaks the parse leaves the whole translation unit as that seed
5771        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5772        // macro-error-class recovery reads.
5773        if node.is_error() {
5774            self.visit_object_macro_error_classes(node, &scope);
5775        }
5776        self.run_container_work(node, scope, ancestry);
5777        self.drain_partitioned_regions();
5778    }
5779
5780    /// Drain reparsed containers after their primary walk has indexed them.
5781    /// Recovered nodes retain their own tree and source context while they are
5782    /// visited; only then are their byte ranges marked consumed so a shared
5783    /// primary event stream does not visit the same declarations twice.
5784    fn drain_partitioned_regions(&mut self) {
5785        let mut consumed = Vec::new();
5786        while let Some(CppPartitionedRegion {
5787            tree,
5788            range,
5789            scope,
5790            recovery,
5791            source_context,
5792        }) = self.partitioned_regions.pop()
5793        {
5794            let root = tree.root_node();
5795            let container = root
5796                .descendant_for_byte_range(range.start, range.end)
5797                .expect("the queued container belongs to this tree");
5798            assert_eq!(container.byte_range(), range);
5799            let ancestry = self.recovery_ancestry(root);
5800            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5801                visitor.run_container_work(container, scope, &ancestry)
5802            });
5803            // A recovered walk may enqueue another partition inside this
5804            // region. Delay the outer claim until all nested regions have
5805            // drained, or their nodes would be skipped as already consumed.
5806            consumed.push((range.start, range.end));
5807        }
5808        self.consumed_fragment_regions.extend(consumed);
5809    }
5810
5811    /// Whether a work node lies entirely inside a byte region consumed by a
5812    /// fragmented export-class recovery (#938); such nodes were already indexed
5813    /// as members of the recovered class by the region reparse.
5814    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5815        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5816    }
5817
5818    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5819    /// candidate a recovery is about to reparse but has not yet turned into a
5820    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5821    /// prior structured recovery.
5822    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5823        self.consumed_fragment_regions
5824            .iter()
5825            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5826    }
5827
5828    /// Drive the container work loop from an explicit seed scope to completion. The
5829    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5830    /// stays alive for the whole traversal.
5831    ///
5832    /// Every ancestor question this walk asks is answered from `ancestry`, which
5833    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5834    /// node's position in the tree, which made a generated header with thousands
5835    /// of top-level declarations quadratic (#2361). The index is the caller's
5836    /// because it outlives any one walk: the file's tree is walked twice when a
5837    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5838    /// builds its own index for its own tree.
5839    fn run_container_work<'tree>(
5840        &mut self,
5841        node: Node<'tree>,
5842        scope: ScopeInfo,
5843        ancestry: &ParentIndex<'tree>,
5844    ) {
5845        self.drain_cpp_work(
5846            vec![CppWork::Container(CppContainer { node, scope })],
5847            ancestry,
5848        );
5849    }
5850
5851    /// The work loop itself, from whatever seed the caller built.
5852    ///
5853    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5854    /// that must walk only part of a reparsed tree seeds it with the sibling
5855    /// range it may walk instead, which keeps the `using namespace X;` scope
5856    /// accumulation `advance_cpp_siblings` performs.
5857    fn drain_cpp_work<'tree>(
5858        &mut self,
5859        mut stack: Vec<CppWork<'tree>>,
5860        ancestry: &ParentIndex<'tree>,
5861    ) {
5862        while let Some(work) = stack.pop() {
5863            match work {
5864                CppWork::Container(container) => {
5865                    push_cpp_container_work(container.node, container.scope, &mut stack);
5866                }
5867                CppWork::Siblings(siblings) => {
5868                    advance_cpp_siblings(siblings, self.source, &mut stack);
5869                }
5870                CppWork::Node(work) => {
5871                    if self.node_is_inside_consumed_fragment(work.node) {
5872                        continue;
5873                    }
5874                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5875                }
5876            }
5877        }
5878    }
5879
5880    /// Drain nodes selected by a bounded recovery walk without filtering them
5881    /// against consumed regions from the primary tree.
5882    fn drain_cpp_recovery_work<'tree>(
5883        &mut self,
5884        mut stack: Vec<CppWork<'tree>>,
5885        ancestry: &ParentIndex<'tree>,
5886    ) {
5887        while let Some(work) = stack.pop() {
5888            match work {
5889                CppWork::Container(container) => {
5890                    push_cpp_container_work(container.node, container.scope, &mut stack);
5891                }
5892                CppWork::Siblings(siblings) => {
5893                    advance_cpp_siblings(siblings, self.source, &mut stack);
5894                }
5895                CppWork::Node(work) => {
5896                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5897                }
5898            }
5899        }
5900    }
5901
5902    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5903    /// it only when the entire region is member-shaped. This validation must happen
5904    /// before registering the recovered class because a rejected speculative range
5905    /// must not leak into the ordinary recovery path.
5906    fn reparse_fragmented_export_class_members(
5907        &self,
5908        fragmented: &FragmentedExportBody,
5909        class_name: &str,
5910    ) -> Option<FragmentedExportMembers> {
5911        if fragmented.reparse_start >= fragmented.reparse_end {
5912            return None;
5913        }
5914        let tree = cpp_reparse_fragmented_class_body(
5915            self.source,
5916            fragmented.reparse_start,
5917            fragmented.reparse_end,
5918        )?;
5919        if cpp_reparsed_members_are_indexable_for_class(
5920            tree.root_node(),
5921            self.source,
5922            Some(class_name),
5923        ) {
5924            return Some(FragmentedExportMembers::Complete(tree));
5925        }
5926        let has_conditional_constructor = {
5927            let root = tree.root_node();
5928            let mut cursor = root.walk();
5929            root.named_children(&mut cursor).any(|child| {
5930                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5931            })
5932        };
5933        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5934    }
5935
5936    /// Index an already validated fragmented body as members of `class_unit`. The
5937    /// region reparse keeps each member's exact original byte and line positions.
5938    fn visit_fragmented_export_class_members(
5939        &mut self,
5940        origin: Node<'_>,
5941        outcome: FragmentedExportMembers,
5942        class_unit: CodeUnit,
5943        scope: &ScopeInfo,
5944    ) -> bool {
5945        self.with_recovery_source_context(origin, |visitor| {
5946            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5947        })
5948    }
5949
5950    fn visit_fragmented_export_class_members_in_context(
5951        &mut self,
5952        outcome: FragmentedExportMembers,
5953        class_unit: CodeUnit,
5954        scope: &ScopeInfo,
5955    ) -> bool {
5956        let (tree, complete) = match outcome {
5957            FragmentedExportMembers::Complete(tree) => (tree, true),
5958            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5959        };
5960        let root = tree.root_node();
5961        let class_name = class_unit.identifier().to_string();
5962        let member_scope = ScopeInfo {
5963            // A recovered export-macro class may borrow its namespace from an
5964            // earlier forward declaration even when the malformed node itself
5965            // sits at file scope. Use the recovered class identity as the
5966            // authoritative package for reparsed members as well.
5967            package_name: class_unit.package_name().to_string(),
5968            module: scope.module.clone(),
5969            class_unit: Some(class_unit),
5970            template_signature: scope.template_signature.clone(),
5971            template_metadata: None,
5972            declarations_are_fields: true,
5973            recovered_specialization_member_scope: false,
5974            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5975        };
5976        let reparsed_ancestry = self.recovery_ancestry(root);
5977        if !complete {
5978            // A conditional beginning immediately after an access label can
5979            // fragment one constructor declaration while leaving the rest of
5980            // the class body as unsafe statement soup. Recover only that
5981            // structurally proven constructor and leave the outer-tree
5982            // siblings unconsumed for their ordinary walk.
5983            let mut cursor = root.walk();
5984            let constructors = root
5985                .named_children(&mut cursor)
5986                .filter_map(|child| {
5987                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
5988                })
5989                .collect::<Vec<_>>();
5990            // The reparsed region is its own tree, so this drain walks it with
5991            // its own parent index.
5992            for constructor in constructors {
5993                let mut stack = Vec::new();
5994                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
5995                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
5996            }
5997            return false;
5998        }
5999        let mut cursor = root.walk();
6000        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6001        for member in members.iter().copied().filter(|member| {
6002            cpp_reparsed_defaulted_constructor_is_indexable(
6003                *member,
6004                &members,
6005                Some(class_name.as_str()),
6006                self.source,
6007            )
6008        }) {
6009            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6010        }
6011        for member in members.iter().copied().filter(|member| {
6012            cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6013        }) {
6014            self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6015        }
6016        // The reparsed region is its own tree, so this walk indexes it itself.
6017        self.run_container_work(root, member_scope, &reparsed_ancestry);
6018        true
6019    }
6020
6021    fn visit_recovered_fragment_constructor<'tree>(
6022        &mut self,
6023        range: std::ops::Range<usize>,
6024        constructor_body: Node<'tree>,
6025        class_declaration: Node<'tree>,
6026        class_unit: &CodeUnit,
6027        scope: &ScopeInfo,
6028        ancestry: &ParentIndex<'tree>,
6029    ) {
6030        self.with_recovery_source_context(class_declaration, |visitor| {
6031            visitor.visit_recovered_fragment_constructor_in_context(
6032                range,
6033                constructor_body,
6034                class_declaration,
6035                class_unit,
6036                scope,
6037                ancestry,
6038            );
6039        });
6040    }
6041
6042    fn visit_recovered_fragment_constructor_in_context<'tree>(
6043        &mut self,
6044        range: std::ops::Range<usize>,
6045        constructor_body: Node<'tree>,
6046        class_declaration: Node<'tree>,
6047        class_unit: &CodeUnit,
6048        scope: &ScopeInfo,
6049        ancestry: &ParentIndex<'tree>,
6050    ) {
6051        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6052            return;
6053        };
6054        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6055            tree.root_node(),
6056            range.start,
6057            class_unit.identifier(),
6058            self.source,
6059        ) else {
6060            return;
6061        };
6062        let member_scope = ScopeInfo {
6063            package_name: class_unit.package_name().to_string(),
6064            module: scope.module.clone(),
6065            class_unit: Some(class_unit.clone()),
6066            template_signature: scope.template_signature.clone(),
6067            template_metadata: None,
6068            declarations_are_fields: true,
6069            recovered_specialization_member_scope: false,
6070            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6071        };
6072        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6073        else {
6074            return;
6075        };
6076        debug_assert_eq!(function.name, class_unit.identifier());
6077        let code_unit = function.code_unit(self.file.clone());
6078        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6079        self.add_declaration_with_range(
6080            &recovered_ancestry,
6081            code_unit.clone(),
6082            Range {
6083                start_byte: function_declarator.start_byte(),
6084                end_byte: constructor_body.end_byte(),
6085                start_line: function_declarator.start_position().row + 1,
6086                end_line: constructor_body.end_position().row + 1,
6087            },
6088            Some(function_declarator),
6089            None,
6090            None,
6091        );
6092        let declaration = *self.source_links[&code_unit]
6093            .last()
6094            .expect("recovered constructor source");
6095        {
6096            let mut properties = self.source_properties.borrow_mut();
6097            let fact = properties
6098                .get_mut(&declaration)
6099                .expect("recovered constructor source fact");
6100            fact.occurrence_role =
6101                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6102            fact.callable_is_constructor = true;
6103        }
6104        self.add_signature_with_metadata(
6105            code_unit.clone(),
6106            cpp_signature_metadata(
6107                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6108                function_declarator,
6109                cpp_callable_linkage(class_declaration, self.source, ancestry),
6110                self.source,
6111                &recovered_ancestry,
6112            )
6113            .with_declaration_only(false),
6114        );
6115        self.parsed.add_child(class_unit.clone(), code_unit);
6116    }
6117
6118    fn visit_recovered_fragment_prefix_members<'tree>(
6119        &mut self,
6120        origin: Node<'_>,
6121        root: Node<'tree>,
6122        constructor_start: usize,
6123        class_unit: &CodeUnit,
6124        scope: &ScopeInfo,
6125        ancestry: &ParentIndex<'tree>,
6126    ) {
6127        self.with_recovery_source_context(origin, |visitor| {
6128            visitor.visit_recovered_fragment_prefix_members_in_context(
6129                root,
6130                constructor_start,
6131                class_unit,
6132                scope,
6133                ancestry,
6134            );
6135        });
6136    }
6137
6138    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6139        &mut self,
6140        root: Node<'tree>,
6141        constructor_start: usize,
6142        class_unit: &CodeUnit,
6143        scope: &ScopeInfo,
6144        ancestry: &ParentIndex<'tree>,
6145    ) {
6146        let member_scope = ScopeInfo {
6147            package_name: class_unit.package_name().to_string(),
6148            module: scope.module.clone(),
6149            class_unit: Some(class_unit.clone()),
6150            template_signature: scope.template_signature.clone(),
6151            template_metadata: None,
6152            declarations_are_fields: true,
6153            recovered_specialization_member_scope: false,
6154            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6155        };
6156        let mut root_cursor = root.walk();
6157        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6158        let mut stack = vec![root];
6159        while let Some(current) = stack.pop() {
6160            if current.kind() == "comment" || current.start_byte() >= constructor_start {
6161                continue;
6162            }
6163            if current
6164                .parent()
6165                .is_some_and(|parent| same_node(parent, root))
6166                && cpp_reparsed_defaulted_constructor_is_indexable(
6167                    current,
6168                    &members,
6169                    Some(class_unit.identifier()),
6170                    self.source,
6171                )
6172            {
6173                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6174                continue;
6175            }
6176            if current.end_byte() <= constructor_start
6177                && current.kind() != "translation_unit"
6178                && current.kind() != "labeled_statement"
6179                && current.kind() != "ERROR"
6180            {
6181                let mut work_stack = Vec::new();
6182                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6183                self.drain_cpp_recovery_work(work_stack, ancestry);
6184                continue;
6185            }
6186            if matches!(
6187                current.kind(),
6188                "translation_unit" | "labeled_statement" | "ERROR"
6189            ) {
6190                let mut cursor = current.walk();
6191                stack.extend(current.named_children(&mut cursor));
6192            }
6193        }
6194    }
6195
6196    fn visit_recovered_defaulted_constructor(
6197        &mut self,
6198        statement: Node<'_>,
6199        scope: &ScopeInfo,
6200        ancestry: &ParentIndex<'_>,
6201    ) {
6202        let Some(parent) = scope.class_unit.clone() else {
6203            return;
6204        };
6205        let Some(assignment) = statement.named_child(0) else {
6206            return;
6207        };
6208        let Some(call) = assignment.child_by_field_name("left") else {
6209            return;
6210        };
6211        let Some(function_name) = call.child_by_field_name("function") else {
6212            return;
6213        };
6214        let Some(arguments) = call.child_by_field_name("arguments") else {
6215            return;
6216        };
6217        if function_name.kind() != "identifier"
6218            || node_text(function_name, self.source) != parent.identifier()
6219            || arguments.kind() != "argument_list"
6220            || arguments.named_child_count() != 0
6221        {
6222            return;
6223        }
6224        let function = FunctionInfo {
6225            package_name: scope.package_name.clone(),
6226            owner: Some(CppMemberOwner::Unit(parent.clone())),
6227            name: parent.identifier().to_string(),
6228            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6229        };
6230        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6231        if self.parsed.contains_declaration(&code_unit) {
6232            return;
6233        }
6234        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6235        let metadata = SignatureMetadata::new(
6236            normalize_cpp_whitespace(node_text(statement, self.source)),
6237            Vec::new(),
6238        )
6239        .with_declaration_only(true)
6240        .with_callable_arity(CallableArity::exact(0))
6241        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6242        // The reparse proves class ownership, but carries no original class
6243        // access ladder. Preserve unknown access rather than guessing it.
6244        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6245        self.add_signature_with_metadata(code_unit.clone(), metadata);
6246        if let Some(declaration) = self
6247            .source_links
6248            .get(&code_unit)
6249            .and_then(|links| links.last())
6250            .copied()
6251        {
6252            let mut properties = self.source_properties.borrow_mut();
6253            let fact = properties
6254                .get_mut(&declaration)
6255                .expect("recovered defaulted constructor source fact");
6256            fact.occurrence_role =
6257                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6258            fact.callable_is_constructor = true;
6259        }
6260        self.parsed.add_child(parent, code_unit);
6261    }
6262
6263    /// Recover a constructor declaration a statement-context class-body
6264    /// reparse read as a bare call statement (`value(pn_data_t* d);`). The
6265    /// statement is validated and its parameters read from a synthetic class
6266    /// context, where the constructor declaration parses as a real function
6267    /// declarator; the declaration itself is minted from the original
6268    /// statement node, so every range keeps its original byte positions.
6269    fn visit_bare_constructor_expression<'tree>(
6270        &mut self,
6271        statement: Node<'tree>,
6272        scope: &ScopeInfo,
6273        ancestry: &ParentIndex<'tree>,
6274    ) {
6275        let Some(parent) = scope.class_unit.clone() else {
6276            return;
6277        };
6278        const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6279        let region = &self.source[statement.start_byte()..statement.end_byte()];
6280        let synthetic = format!("{PREFIX}{region} }};");
6281        let mut parser = tree_sitter::Parser::new();
6282        if parser
6283            .set_language(&tree_sitter_cpp::LANGUAGE.into())
6284            .is_err()
6285        {
6286            return;
6287        }
6288        let Some(tree) = parser.parse(&synthetic, None) else {
6289            return;
6290        };
6291        let root = tree.root_node();
6292        if root.has_error() {
6293            return;
6294        }
6295        let mut declarator = None;
6296        let mut stack = vec![root];
6297        while let Some(current) = stack.pop() {
6298            if current.kind() == "function_declarator" {
6299                if declarator.is_some() {
6300                    return;
6301                }
6302                declarator = Some(current);
6303                continue;
6304            }
6305            let mut cursor = current.walk();
6306            stack.extend(current.named_children(&mut cursor));
6307        }
6308        let Some(declarator) = declarator else {
6309            return;
6310        };
6311        let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6312            return;
6313        };
6314        let signature = cpp_parameter_signature(parameters_node, &synthetic);
6315        let mut parameter_labels = Vec::new();
6316        let mut parameter_cursor = parameters_node.walk();
6317        for parameter in parameters_node.named_children(&mut parameter_cursor) {
6318            if matches!(
6319                parameter.kind(),
6320                "parameter_declaration" | "optional_parameter_declaration"
6321            ) && let Some(name) = parameter
6322                .child_by_field_name("declarator")
6323                .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6324            {
6325                parameter_labels.push(name);
6326            }
6327        }
6328        let arity = parameter_labels.len();
6329        let function = FunctionInfo {
6330            package_name: scope.package_name.clone(),
6331            owner: Some(CppMemberOwner::Unit(parent.clone())),
6332            name: parent.identifier().to_string(),
6333            signature,
6334        };
6335        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6336        if self.parsed.contains_declaration(&code_unit) {
6337            return;
6338        }
6339        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6340        let metadata = SignatureMetadata::with_parameter_labels(
6341            normalize_cpp_whitespace(node_text(statement, self.source)),
6342            parameter_labels,
6343        )
6344        .with_declaration_only(true)
6345        .with_callable_arity(CallableArity::exact(arity))
6346        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6347        // The reparse proves class ownership, but carries no original
6348        // class access ladder. Preserve unknown access rather than
6349        // guessing it.
6350        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6351        self.add_signature_with_metadata(code_unit.clone(), metadata);
6352        if let Some(declaration) = self
6353            .source_links
6354            .get(&code_unit)
6355            .and_then(|links| links.last())
6356            .copied()
6357        {
6358            let mut properties = self.source_properties.borrow_mut();
6359            let fact = properties
6360                .get_mut(&declaration)
6361                .expect("recovered constructor source fact");
6362            fact.occurrence_role =
6363                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6364            fact.callable_is_constructor = true;
6365        }
6366        self.parsed.add_child(parent, code_unit);
6367    }
6368
6369    /// Capture ancestors only when a proven recovery enters another tree. The
6370    /// ordinary declaration driver never performs this additional ancestor walk.
6371    fn with_recovery_source_context<T>(
6372        &mut self,
6373        node: Node<'_>,
6374        walk: impl FnOnce(&mut Self) -> T,
6375    ) -> T {
6376        self.with_recovery_source_context_at(node, node, walk)
6377    }
6378
6379    /// [`Self::with_recovery_source_context`] for a recovery whose content
6380    /// begins inside `node` at `preprocessor_anchor`: the lexical owners come
6381    /// from `node`, the preprocessor facts from the anchor. The two differ when
6382    /// the malformed node swallowed a branch directive that precedes the
6383    /// recovered content.
6384    fn with_recovery_source_context_at<T>(
6385        &mut self,
6386        node: Node<'_>,
6387        preprocessor_anchor: Node<'_>,
6388        walk: impl FnOnce(&mut Self) -> T,
6389    ) -> T {
6390        use crate::graph::resolver::{
6391            enclosing_namespace_context, merge_preprocessor_guards,
6392            preprocessor_conditional_family_for_declaration_with_cache,
6393            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6394        };
6395        let previous = self.active_source_context.take();
6396        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6397        let inherited = (!primary).then_some(previous.as_ref()).flatten();
6398        let local_guards = preprocessor_guard_environment_with_cache(
6399            preprocessor_anchor,
6400            self.source,
6401            &mut self.preprocessor_context_cache,
6402        );
6403        let guards = match inherited {
6404            Some(outer) => outer
6405                .guards
6406                .as_ref()
6407                .zip(local_guards.as_ref())
6408                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6409            None => local_guards,
6410        };
6411        let local_namespace = enclosing_namespace_context(node, self.source);
6412        let namespace = match (
6413            inherited.and_then(|outer| outer.child_namespace.as_ref()),
6414            local_namespace,
6415        ) {
6416            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6417            (Some(outer), None) => Some(outer.clone()),
6418            (None, local) => local,
6419        };
6420        let outer_lexical_scope =
6421            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6422        let mut local_owners = Vec::new();
6423        let mut parent = node.parent();
6424        while let Some(ancestor) = parent {
6425            if matches!(
6426                ancestor.kind(),
6427                "namespace_definition"
6428                    | "class_specifier"
6429                    | "struct_specifier"
6430                    | "union_specifier"
6431                    | "enum_specifier"
6432            ) && let Some(name) = ancestor.child_by_field_name("name")
6433            {
6434                local_owners.push(node_text(name, self.source).to_owned());
6435            }
6436            parent = ancestor.parent();
6437        }
6438        local_owners.reverse();
6439        let mut child_lexical_scope = outer_lexical_scope.clone();
6440        child_lexical_scope.extend(local_owners);
6441        if matches!(
6442            node.kind(),
6443            "namespace_definition"
6444                | "class_specifier"
6445                | "struct_specifier"
6446                | "union_specifier"
6447                | "enum_specifier"
6448        ) && let Some(name) = node.child_by_field_name("name")
6449        {
6450            child_lexical_scope.push(node_text(name, self.source).to_owned());
6451        }
6452        let child_namespace = if node.kind() == "namespace_definition" {
6453            node.child_by_field_name("name")
6454                .map(|name| {
6455                    let local = node_text(name, self.source);
6456                    namespace
6457                        .as_ref()
6458                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6459                })
6460                .or_else(|| namespace.clone())
6461        } else {
6462            namespace.clone()
6463        };
6464        let intern = |family: Node<'_>| {
6465            let mut collector = self.source_collector.borrow_mut();
6466            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6467                collector.intern_node(family)
6468            } else {
6469                collector
6470                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6471            }
6472        };
6473        let family = preprocessor_conditional_family_node(preprocessor_anchor)
6474            .map(intern)
6475            .or_else(|| inherited.and_then(|outer| outer.family));
6476        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6477            preprocessor_anchor,
6478            &mut self.preprocessor_context_cache,
6479        )
6480        .map(intern)
6481        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6482        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6483            preprocessor_anchor,
6484            self.source,
6485            &mut self.preprocessor_context_cache,
6486        );
6487        let callable_guard_completion_byte = match inherited {
6488            Some(outer) => local_completion
6489                .zip(outer.callable_guard_completion_byte)
6490                .map(|(local, outer)| local.max(outer)),
6491            None => local_completion,
6492        };
6493        self.active_source_context = Some(CppActiveSourceContext {
6494            guards,
6495            namespace: child_namespace.clone(),
6496            child_namespace,
6497            outer_lexical_scope: child_lexical_scope.clone(),
6498            child_lexical_scope,
6499            family,
6500            exhaustive_family,
6501            callable_guard_completion_byte,
6502        });
6503        let result = walk(self);
6504        self.active_source_context = previous;
6505        result
6506    }
6507
6508    fn visit_node<'tree>(
6509        &mut self,
6510        node: Node<'tree>,
6511        scope: &ScopeInfo,
6512        stack: &mut Vec<CppWork<'tree>>,
6513        ancestry: &ParentIndex<'tree>,
6514    ) {
6515        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6516            self.visit_node(node, &recovered_scope, stack, ancestry);
6517            return;
6518        }
6519        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6520            self.visit_node(node, &recovered_scope, stack, ancestry);
6521            return;
6522        }
6523        if scope.class_unit.is_some()
6524            && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6525        {
6526            self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6527            return;
6528        }
6529        // Fragmented-class recovery below may consume a malformed function
6530        // envelope before the ordinary kind dispatch runs. Recover any
6531        // export-macro class embedded in that envelope first; the strict class
6532        // head/base/body predicate is independent of which later recovery owns
6533        // the surrounding parser fragment.
6534        if node.kind() == "function_definition" && node.has_error() {
6535            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6536        }
6537        if let Some(FragmentedClassRecovery {
6538            declaration_node: class_node,
6539            name,
6540            raw_supertypes,
6541            body: fragmented,
6542        }) = fragmented_class_body(node, self.source)
6543        {
6544            let displaced_namespace_items =
6545                displaced_fragment_namespace_geometry(node, self.source)
6546                    .map(|boundary| boundary.namespace_items)
6547                    .unwrap_or_default();
6548            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6549            let mut class_stack = Vec::new();
6550            // When the full body cannot be safely reparsed, the original class
6551            // node still proves ownership for its parser-visible prefix.
6552            let parser_visible_body =
6553                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6554                    .then(|| cpp_body_node(class_node))
6555                    .flatten();
6556            let class_unit = self.visit_named_class_like_shape(
6557                class_node,
6558                name,
6559                parser_visible_body,
6560                true,
6561                Some(fragmented.class_range),
6562                Some(raw_supertypes),
6563                Some(extract_cpp_base_facts(class_node, self.source)),
6564                scope,
6565                &mut class_stack,
6566                ancestry,
6567            );
6568            let member_scope = ScopeInfo {
6569                package_name: class_unit.package_name().to_string(),
6570                module: scope.module.clone(),
6571                class_unit: Some(class_unit.clone()),
6572                template_signature: scope.template_signature.clone(),
6573                template_metadata: None,
6574                declarations_are_fields: true,
6575                recovered_specialization_member_scope: false,
6576                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6577            };
6578            let complete = outcome.is_some_and(|outcome| {
6579                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6580            });
6581            if complete {
6582                self.consumed_fragment_regions
6583                    .push((node.start_byte(), fragmented.class_range.end_byte));
6584            } else {
6585                // A macro-constrained member can make the full body reparse
6586                // unsafe while tree-sitter still exposes later class members
6587                // as bounded siblings up to the displaced `}`/`;`. Keep the
6588                // structurally proven class/base declaration and re-own those
6589                // sibling nodes under it. They retain their original parser
6590                // nodes and exact ranges; the close boundary comes solely from
6591                // `fragmented_class_body`.
6592                // Template wrappers put the escaped members beside the
6593                // template rather than beside its malformed declaration.
6594                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6595                    if candidate.start_byte() >= fragmented.reparse_end {
6596                        break;
6597                    }
6598                    if cpp_fragment_sibling_is_class_member(
6599                        candidate,
6600                        fragmented.reparse_end,
6601                        self.source,
6602                    ) {
6603                        self.recovered_class_sibling_scopes
6604                            .insert(candidate.id(), member_scope.clone());
6605                    }
6606                }
6607                // The macro-declaration grammar keeps the class head and its
6608                // parser-visible prefix members inside the fragmented
6609                // container itself, where there is no separate body node for
6610                // `visit_named_class_like_shape` to seed. Walk the container
6611                // with the member scope so those prefix members are owned by
6612                // the class; the head's bare tokens mint nothing on their own.
6613                if class_node.kind() == "ERROR" && class_node.has_error() {
6614                    stack.push(CppWork::Container(CppContainer {
6615                        node: class_node,
6616                        scope: member_scope,
6617                    }));
6618                }
6619            }
6620            for item in displaced_namespace_items {
6621                self.recovered_class_sibling_scopes
6622                    .insert(item.id(), scope.clone());
6623            }
6624            stack.extend(class_stack);
6625            return;
6626        }
6627        if self.visit_folded_aggregate(node, scope) {
6628            return;
6629        }
6630        match node.kind() {
6631            "template_declaration" => {
6632                if let Some(recovered) =
6633                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6634                {
6635                    let mut template_scope = scope.clone();
6636                    template_scope.template_signature =
6637                        cpp_template_signature(node, recovered.declaration_node, self.source);
6638                    template_scope.template_metadata =
6639                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6640                    let raw_supertypes =
6641                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6642                    let mut class_stack = Vec::new();
6643                    let class_unit = self.visit_named_class_like_shape(
6644                        recovered.class_node,
6645                        recovered.name,
6646                        Some(recovered.body),
6647                        true,
6648                        Some(recovered.range),
6649                        raw_supertypes,
6650                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6651                        &template_scope,
6652                        &mut class_stack,
6653                        ancestry,
6654                    );
6655                    self.parsed.record_materialization(
6656                        MaterializationRecord::RecoveredDeclaration {
6657                            recovery: recovered.range,
6658                            unit: class_unit.clone(),
6659                        },
6660                    );
6661                    let member_scope = ScopeInfo {
6662                        package_name: template_scope.package_name.clone(),
6663                        module: template_scope.module.clone(),
6664                        class_unit: Some(class_unit.clone()),
6665                        template_signature: template_scope.template_signature.clone(),
6666                        template_metadata: None,
6667                        declarations_are_fields: true,
6668                        recovered_specialization_member_scope: recovered
6669                            .class_node
6670                            .child_by_field_name("name")
6671                            .is_some_and(|name| name.kind() == "template_type"),
6672                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6673                    };
6674                    for tail_member in recovered.tail_members.into_iter().rev() {
6675                        stack.push(CppWork::Node(CppNodeWork {
6676                            node: tail_member,
6677                            scope: member_scope.clone(),
6678                        }));
6679                    }
6680                    stack.extend(class_stack);
6681                    for sibling in recovered.member_siblings {
6682                        self.recovered_class_sibling_scopes
6683                            .insert(sibling.id(), member_scope.clone());
6684                    }
6685                    return;
6686                }
6687                for index in (0..node.named_child_count()).rev() {
6688                    let Some(child) = node.named_child(index) else {
6689                        continue;
6690                    };
6691                    if matches!(
6692                        child.kind(),
6693                        "class_specifier"
6694                            | "struct_specifier"
6695                            | "union_specifier"
6696                            | "enum_specifier"
6697                            | "function_definition"
6698                            | "declaration"
6699                            | "field_declaration"
6700                            | "alias_declaration"
6701                            | "namespace_definition"
6702                    ) {
6703                        let mut template_scope = scope.clone();
6704                        template_scope.template_signature =
6705                            cpp_template_signature(node, child, self.source);
6706                        template_scope.template_metadata =
6707                            cpp_template_metadata(node, child, self.source, ancestry);
6708                        if let Some(recovered) = recover_fragmented_partial_specialization(
6709                            node,
6710                            child,
6711                            self.source,
6712                            ancestry,
6713                        ) {
6714                            let code_unit = self.visit_named_class_like_shape(
6715                                recovered.declaration_node,
6716                                recovered.name,
6717                                None,
6718                                true,
6719                                Some(recovered.range),
6720                                None,
6721                                None,
6722                                &template_scope,
6723                                stack,
6724                                ancestry,
6725                            );
6726                            self.parsed.record_materialization(
6727                                MaterializationRecord::RecoveredDeclaration {
6728                                    recovery: recovered.range,
6729                                    unit: code_unit.clone(),
6730                                },
6731                            );
6732                            let mut member_scope = template_scope.clone();
6733                            member_scope.class_unit = Some(code_unit);
6734                            member_scope.declarations_are_fields = true;
6735                            member_scope.recovered_specialization_member_scope = true;
6736                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6737                                stack.push(CppWork::Node(CppNodeWork {
6738                                    node: prefix_member,
6739                                    scope: member_scope.clone(),
6740                                }));
6741                            }
6742                            for sibling in recovered.member_siblings {
6743                                self.recovered_class_sibling_scopes
6744                                    .insert(sibling.id(), member_scope.clone());
6745                            }
6746                            for following in recovered.following_declarations.into_iter().rev() {
6747                                stack.push(CppWork::Node(CppNodeWork {
6748                                    node: following,
6749                                    scope: scope.clone(),
6750                                }));
6751                            }
6752                            return;
6753                        }
6754                        stack.push(CppWork::Node(CppNodeWork {
6755                            node: child,
6756                            scope: template_scope,
6757                        }));
6758                    }
6759                }
6760            }
6761            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6762            "linkage_specification" => {
6763                if let Some(body) = cpp_body_node(node) {
6764                    // A linkage specification either wraps a direct declaration or a
6765                    // braced declaration_list. Visit the direct item itself so
6766                    // declaration recoveries still run on its original syntax node.
6767                    let work = if body.kind() == "declaration_list" {
6768                        CppWork::Container(CppContainer {
6769                            node: body,
6770                            scope: scope.clone(),
6771                        })
6772                    } else {
6773                        CppWork::Node(CppNodeWork {
6774                            node: body,
6775                            scope: scope.clone(),
6776                        })
6777                    };
6778                    stack.push(work);
6779                } else {
6780                    stack.push(CppWork::Container(CppContainer {
6781                        node,
6782                        scope: scope.clone(),
6783                    }));
6784                }
6785            }
6786            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6787                self.visit_class_like(node, scope, stack, ancestry)
6788            }
6789            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6790            // A bare namespace-begin sentinel can make tree-sitter promote the
6791            // wrapped declaration to an ERROR node instead of the usual bogus
6792            // function_definition envelope. Keep the recovery entry point on
6793            // the same structured path for both shapes; ordinary ERROR nodes
6794            // retain their declaration-preserving wrapper traversal when the
6795            // sentinel predicate does not match.
6796            "ERROR" => {
6797                self.visit_object_macro_error_classes(node, scope);
6798                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6799                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6800                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6801                        return;
6802                    }
6803                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6804                        return;
6805                    }
6806                    self.visit_macro_swallowed_function_declarations(node, scope);
6807                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6808                    self.visit_stranded_class_members(node, scope, ancestry);
6809                    stack.push(CppWork::Container(CppContainer {
6810                        node,
6811                        scope: scope.clone(),
6812                    }));
6813                }
6814            }
6815            "declaration" => {
6816                if node.has_error() {
6817                    // Object-like macro lines before a function-like
6818                    // export-macro class head keep the head in a `declaration`
6819                    // rather than in an `ERROR` (#2924). It is the same head
6820                    // and the same following-sibling body, so it takes the same
6821                    // recovery; without it the head's macro invocation indexes
6822                    // as a field named after the macro.
6823                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6824                        return;
6825                    }
6826                }
6827                // The macro-declaration grammar reads a K&R prototype macro
6828                // line as a clean declaration with the name and the macro
6829                // displaced into `macro_modifier` slots, so the prototype
6830                // candidate gate runs on clean declarations too; the candidates
6831                // themselves remain the admission proof.
6832                self.visit_prototype_macro_declarations(node, scope);
6833                if self.node_is_inside_consumed_fragment(node) {
6834                    // The whole declaration was a recovered K&R
6835                    // prototype macro invocation (issue #2932); the
6836                    // reparse above already indexed its Function.
6837                    // Falling through to the ordinary declaration
6838                    // visitor would additionally mint the swapped-field
6839                    // wreckage this recovery exists to replace.
6840                    return;
6841                }
6842                if scope.class_unit.is_some()
6843                    && scope.declarations_are_fields
6844                    && scope.recovered_specialization_member_scope
6845                    && let Some(alias_name) =
6846                        recovered_using_declaration_alias_name(node, self.source)
6847                {
6848                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6849                } else {
6850                    self.visit_declaration(
6851                        node,
6852                        scope,
6853                        scope.declarations_are_fields,
6854                        stack,
6855                        ancestry,
6856                    )
6857                }
6858            }
6859            // A K&R prototype macro invocation with a pointer return type and a
6860            // simple argument list parses with no ERROR node at all: tree-sitter
6861            // reads `T *name _(( args ));` as a multiplication of a type by a
6862            // call (`T * name_(...)`), wrapped in an `expression_statement`
6863            // that `has_error()` only because of a `MISSING "::"` inside it
6864            // (issue #2932). Nothing else mints a declaration from an
6865            // `expression_statement` at declaration scope today, so there is
6866            // no existing behavior to preserve here if this node is not the
6867            // K&R shape; the admission gate inside
6868            // `visit_prototype_macro_declarations` (exactly one clean function
6869            // prototype spanning the recovered run) is what keeps this arm
6870            // safe on an ordinary errorful expression statement that happens
6871            // to contain nested parentheses.
6872            "expression_statement" => {
6873                if node.has_error() {
6874                    self.visit_prototype_macro_declarations(node, scope);
6875                }
6876            }
6877            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
6878            "preproc_call" => self.visit_preproc_call(node, scope),
6879            "type_definition" | "alias_declaration" => {
6880                self.visit_type_declaration(node, scope, stack, ancestry)
6881            }
6882            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6883            // `#include` is collected by the coordinated primary events before the
6884            // walk, so a directive the container walk never reaches -- inside
6885            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6886            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6887            // include claim.
6888            "preproc_include" => {}
6889            // The grammar represents a split extern-C condition as sibling
6890            // markers; declarations between them remain unconditional file
6891            // scope items and need no special declaration metadata.
6892            "preproc_linkage_open" | "preproc_linkage_close" => {}
6893            kind if preserves_declaration_scope_through_wrapper(
6894                kind,
6895                scope.class_unit.is_some(),
6896            ) =>
6897            {
6898                // A preprocessor conditional gates every declaration inside it
6899                // on a configuration this analyzer never evaluates; record the
6900                // interval so declaration state can say so (issue #1476). The
6901                // else/elif branches are children of the `preproc_if` node, so
6902                // recording the openers covers every branch.
6903                if kind == "labeled_statement" {
6904                    self.visit_access_label_constructor(node, scope);
6905                }
6906                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6907                    let mut range = cpp_declaration_range(node);
6908                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6909                        range.end_byte = boundary.end_byte;
6910                        range.end_line = boundary.end_line;
6911                    }
6912                    self.parsed.record_materialization(
6913                        MaterializationRecord::ConfigurationConditional { range },
6914                    );
6915                    if node.has_error() {
6916                        // A malformed export-macro class can close the namespace
6917                        // node early while the enclosing include guard still owns
6918                        // the remaining class-head/body pairs. The ordinary walk
6919                        // cannot carry the lost namespace through those promoted
6920                        // siblings. Scan only structured ERROR nodes in this
6921                        // already-malformed conditional; the pair recovery's
6922                        // exact class/macro/body predicate remains the admission
6923                        // gate, and its namespace lifting restores the owner.
6924                        let mut candidates = vec![node];
6925                        while let Some(candidate) = candidates.pop() {
6926                            // An `ERROR` still inside a namespace body is one
6927                            // the ordinary walk reaches with that namespace in
6928                            // scope. Claiming it here registers the recovered
6929                            // class at file scope and the consumed region then
6930                            // suppresses the walk that would have named it
6931                            // correctly -- which is why Botan's `DL_Group` was
6932                            // `DL_Group` and not `Botan.DL_Group` in the real
6933                            // header, where an include guard wraps the
6934                            // namespace, and was right in a fixture without one
6935                            // (#2552).
6936                            if candidate.kind() == "ERROR"
6937                                && !cpp_is_inside_namespace_body(candidate, ancestry)
6938                                && self.visit_function_like_export_class_pair(
6939                                    candidate, scope, stack, ancestry,
6940                                )
6941                            {
6942                                continue;
6943                            }
6944                            for index in (0..candidate.named_child_count()).rev() {
6945                                candidates.push(
6946                                    candidate
6947                                        .named_child(index)
6948                                        .expect("index below the node's own named child count"),
6949                                );
6950                            }
6951                        }
6952                    }
6953                }
6954                stack.push(CppWork::Container(CppContainer {
6955                    node,
6956                    scope: scope.clone(),
6957                }))
6958            }
6959            // A macro invocation the parser could not group leaves its head in
6960            // whatever slot was free. The `function_definition` and `ERROR`
6961            // arms above try the same recovery on the envelopes they get; a
6962            // flattened run leaves a bare `identifier`/`type_identifier`, and a
6963            // swallowed tail can land in a `parameter_declaration` (#3094).
6964            _ => {
6965                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
6966            }
6967        }
6968    }
6969
6970    fn visit_macro_swallowed_function_declarations<'tree>(
6971        &mut self,
6972        envelope: Node<'tree>,
6973        scope: &ScopeInfo,
6974    ) {
6975        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
6976            || envelope.kind() == "ERROR"
6977                && envelope
6978                    .parent()
6979                    .is_some_and(|parent| parent.kind() == "ERROR")
6980        {
6981            return;
6982        }
6983        let mut stack = (0..envelope.named_child_count())
6984            .filter_map(|index| envelope.named_child(index))
6985            .collect::<Vec<_>>();
6986        while let Some(node) = stack.pop() {
6987            if node.kind() == "function_declarator" {
6988                self.visit_error_swallowed_function_declaration(node, scope);
6989            }
6990            for child in named_children_iter(node) {
6991                stack.push(child);
6992            }
6993        }
6994    }
6995
6996    /// Index the declarations an attribute-like macro invocation swallowed into
6997    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
6998    /// the shape and why the parser produces it.
6999    fn visit_macro_wrapped_declarations<'tree>(
7000        &mut self,
7001        envelope: Node<'tree>,
7002        scope: &ScopeInfo,
7003        ancestry: &ParentIndex<'tree>,
7004    ) {
7005        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7006        if recovered.is_empty() {
7007            return;
7008        }
7009        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7010        self.record_recovered_declarations(recovery, envelope, |visitor| {
7011            for declaration in recovered {
7012                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7013            }
7014        });
7015    }
7016
7017    /// Index the declarations a macro invocation collapsed. See
7018    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
7019    /// produces it. Returns whether it claimed the region beginning at
7020    /// `envelope`.
7021    ///
7022    /// The parser's own nodes cannot be read the way the swallowed tail of the
7023    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
7024    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
7025    /// with no declarator left at all, and the declarators that do survive on
7026    /// the envelope's declarator spine pair one declaration's name with the
7027    /// *next* declaration's parameter list. Reading those would be a guess.
7028    ///
7029    /// The bytes are still ordinary declarations, though, and the collapse is
7030    /// the parser carrying the failure forward from one macro invocation. So
7031    /// reparse the region, walk the items the parser makes of it, and when one
7032    /// item is itself a collapsed run, recover that invocation from its own
7033    /// bytes and resume the scan just past it. Each pass starts later than the
7034    /// last, so the scan is a loop over the invocations that collapse, not over
7035    /// the declarations: `llama.h` needs three passes for 214 declarations.
7036    ///
7037    /// The region can reach past `envelope` into the siblings the parser handed
7038    /// the rest of one invocation, so it is recorded as consumed: those
7039    /// siblings are this recovery's, and the ordinary walk must not read them
7040    /// again. Recording it after the scan, not before, keeps the scan's own
7041    /// reparsed nodes -- which carry their original byte offsets -- visible to
7042    /// the walk it drives.
7043    fn visit_collapsed_macro_declaration_run<'tree>(
7044        &mut self,
7045        envelope: Node<'tree>,
7046        scope: &ScopeInfo,
7047        ancestry: &ParentIndex<'tree>,
7048    ) -> bool {
7049        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7050            return false;
7051        };
7052        let start = envelope.start_byte();
7053        let end = run.region_end;
7054        let recovery = cpp_recovery_window(self.source, start, end);
7055        self.record_recovered_declarations(recovery, envelope, |visitor| {
7056            let mut position = start;
7057            while position < end {
7058                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7059                    return;
7060                };
7061                let root = tree.root_node();
7062                // A region reparse is its own tree and needs its own parent
7063                // index and dependency arena; the caller's answers nothing
7064                // about these nodes.
7065                let ancestry = visitor.recovery_ancestry(root);
7066                let mut cursor = root.walk();
7067                let collapsed =
7068                    root.named_children(&mut cursor)
7069                        .enumerate()
7070                        .find_map(|(index, item)| {
7071                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7072                                .map(|run| (index, item, run))
7073                        });
7074                // Walk only the items before the collapsed one. It swallowed
7075                // the rest of the region, so handing it to the ordinary walk
7076                // would re-enter this recovery on a region that starts where
7077                // this one did.
7078                let mut stack = Vec::new();
7079                push_cpp_sibling_range(
7080                    root,
7081                    0,
7082                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7083                    scope.clone(),
7084                    &mut stack,
7085                );
7086                visitor.drain_cpp_work(stack, &ancestry);
7087                let Some((_, item, run)) = collapsed else {
7088                    return;
7089                };
7090                // On its own bytes the invocation is the declaration-scope
7091                // shape `macro_wrapped_declarations` reads, so its wrapped
7092                // declaration needs no reader of its own here.
7093                if let Some(head) =
7094                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7095                {
7096                    let head_root = head.root_node();
7097                    let head_ancestry = visitor.recovery_ancestry(head_root);
7098                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7099                }
7100                assert!(
7101                    run.invocation_end > position,
7102                    "a collapsed run at {position} must end after the byte the scan resumed \
7103                     from, but ended at {}",
7104                    run.invocation_end
7105                );
7106                position = run.invocation_end;
7107            }
7108        });
7109        self.consumed_fragment_regions.push((start, end));
7110        true
7111    }
7112
7113    /// Index the members written under an access label that a class-scope
7114    /// macro invocation swallowed. See [`cpp_macro_swallowed_access_label`]
7115    /// for the shape.
7116    ///
7117    /// The members are whole in the source but not in this tree, and at file
7118    /// scope a bodiless member such as `Ctor(int a);` reads as a variable
7119    /// declaration, so the reparse keeps the class around them: the class head
7120    /// through its `{`, the label and its members, and the class's `}`, as one
7121    /// included-range parse at the original offsets. That is the parse the
7122    /// class gets with the invocation's bytes left out, which is what the
7123    /// replacement's own declarations would have to be read without anyway.
7124    /// The reparsed label is a real `access_specifier`, so the members carry the
7125    /// access they are written under. Only the members the label swallowed are
7126    /// walked; the class's other members are the ordinary walk's. When the
7127    /// reparse does not read the label back, nothing is published: the
7128    /// field this node would otherwise mint is named after the access keyword.
7129    fn visit_macro_swallowed_access_label_members<'tree>(
7130        &mut self,
7131        node: Node<'tree>,
7132        label: &CppSwallowedAccessLabel<'tree>,
7133        scope: &ScopeInfo,
7134        ancestry: &ParentIndex<'tree>,
7135    ) {
7136        let Some(body) = ancestry.parent(node) else {
7137            return;
7138        };
7139        let Some(class_like) = ancestry.parent(body) else {
7140            return;
7141        };
7142        // The reparse holds the class itself, so the recovery's source context
7143        // is the class's enclosing one; the class's own scope would name it
7144        // twice in the recovered members' lexical paths.
7145        let Some(class_parent) = ancestry.parent(class_like) else {
7146            return;
7147        };
7148        let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7149            return;
7150        };
7151        if body.kind() != "field_declaration_list"
7152            || open.kind() != "{"
7153            || close.kind() != "}"
7154            || close.is_missing()
7155        {
7156            return;
7157        }
7158        let start = label.keyword.start_byte();
7159        let end = node.end_byte();
7160        let Some(tree) = parse_source_ranges_with_cancellation(
7161            &tree_sitter_cpp::LANGUAGE.into(),
7162            self.source,
7163            &[
7164                (class_like.start_byte(), open.end_byte()),
7165                (start, end),
7166                (close.start_byte(), close.end_byte()),
7167            ],
7168            None,
7169        ) else {
7170            return;
7171        };
7172        let root = tree.root_node();
7173        let Some(reparsed_body) = root
7174            .descendant_for_byte_range(open.start_byte(), open.end_byte())
7175            .and_then(|brace| brace.parent())
7176            .filter(|parent| parent.kind() == "field_declaration_list")
7177        else {
7178            return;
7179        };
7180        let mut cursor = reparsed_body.walk();
7181        let members = reparsed_body
7182            .named_children(&mut cursor)
7183            .enumerate()
7184            .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7185            .map(|(index, member)| (index, member.kind(), member.start_byte()))
7186            .collect::<Vec<_>>();
7187        let Some(&(first, "access_specifier", label_start)) = members.first() else {
7188            return;
7189        };
7190        if label_start != start {
7191            return;
7192        }
7193        let last = members.last().expect("the label is a member").0;
7194        let reparsed_ancestry = self.recovery_ancestry(root);
7195        let recovery = cpp_recovery_window(self.source, start, end);
7196        self.record_recovered_declarations(recovery, class_parent, |visitor| {
7197            let mut stack = Vec::new();
7198            push_cpp_sibling_range(
7199                reparsed_body,
7200                first + 1,
7201                last + 1,
7202                scope.clone(),
7203                &mut stack,
7204            );
7205            visitor.drain_cpp_work(stack, &reparsed_ancestry);
7206        });
7207    }
7208
7209    /// Index the members a string-argument attribute macro stranded in an
7210    /// `ERROR` inside a class body.
7211    ///
7212    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
7213    /// grouping of the attributed member and of every member written after it
7214    /// until it recovers. The members are still whole `function_declarator`
7215    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
7216    /// export-macro class such as Botan's `DL_Group` was indexed with no
7217    /// members at all (#2552).
7218    fn visit_stranded_class_members<'tree>(
7219        &mut self,
7220        node: Node<'tree>,
7221        scope: &ScopeInfo,
7222        ancestry: &ParentIndex<'tree>,
7223    ) {
7224        if scope.class_unit.is_none() || !scope.declarations_are_fields {
7225            return;
7226        }
7227        for member in stranded_declaration_run(node, self.source).declarations {
7228            self.add_macro_wrapped_declaration(member, scope, ancestry);
7229        }
7230    }
7231
7232    /// Index the constructor an access label swallowed in a reparsed class
7233    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
7234    ///
7235    /// The declarator is recovered by reparsing from that call to the end of
7236    /// the label's own statement, which is the same offset-preserving region
7237    /// reparse every other recovery here uses, so the recovered nodes carry
7238    /// their true source positions.
7239    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7240        let Some(class_unit) = scope.class_unit.clone() else {
7241            return;
7242        };
7243        if !scope.declarations_are_fields {
7244            return;
7245        }
7246        let class_name = class_unit.identifier().to_string();
7247        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7248        else {
7249            return;
7250        };
7251        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7252            return;
7253        };
7254        let root = tree.root_node();
7255        let Some(declarator) =
7256            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7257        else {
7258            return;
7259        };
7260        let reparsed_ancestry = self.recovery_ancestry(root);
7261        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7262        let range = cpp_declaration_range(definition.unwrap_or(declarator));
7263        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7264        self.record_recovered_declarations(recovery, node, |visitor| {
7265            visitor.add_macro_wrapped_declaration(
7266                MacroWrappedDeclaration {
7267                    declarator,
7268                    range,
7269                    is_static: false,
7270                },
7271                scope,
7272                &reparsed_ancestry,
7273            );
7274        });
7275    }
7276
7277    /// Index the members a string-argument attribute macro stranded inside the
7278    /// `macro_type_specifier` the macro-declaration grammar reads them as (#2552
7279    /// shape 3 under the new grammar).
7280    ///
7281    /// `BOTAN_DEPRECATED("text") explicit Ctor(T); Ctor(U, V);` keeps the macro as
7282    /// a `macro_type_specifier` whose argument region is an `ERROR` holding the
7283    /// string, the attributed member as a `type_descriptor`, and its `;`, while
7284    /// the member after it becomes the specifier's own `type` field. Neither
7285    /// member has a real declarator node -- an abstract function declarator in a
7286    /// `type_descriptor` carries only parameters -- but each member's source range
7287    /// is whole, so reparse it and read the declarator from the reparse, which is
7288    /// the same offset-preserving region reparse every other recovery here uses.
7289    /// The admission gate is the same one every reparse shares: exactly one clean
7290    /// callable declaration spanning the member's range.
7291    fn visit_string_attribute_macro_type_members<'tree>(
7292        &mut self,
7293        node: Node<'tree>,
7294        scope: &ScopeInfo,
7295    ) -> bool {
7296        if node.kind() != "field_declaration" {
7297            return false;
7298        }
7299        let Some(specifier) = node
7300            .child_by_field_name("type")
7301            .filter(|type_node| type_node.kind() == "macro_type_specifier")
7302        else {
7303            return false;
7304        };
7305        if specifier
7306            .child_by_field_name("name")
7307            .is_none_or(|name| name.kind() != "identifier")
7308        {
7309            return false;
7310        }
7311        // The attributed member sits inside the argument `ERROR`: the string's
7312        // quote is the first child, the member's `;` the last, and its
7313        // type-descriptor is in between.
7314        let mut cursor = specifier.walk();
7315        let Some(argument_error) = specifier
7316            .named_children(&mut cursor)
7317            .find(|child| child.kind() == "ERROR")
7318        else {
7319            return false;
7320        };
7321        if argument_error
7322            .child(0)
7323            .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7324            || argument_error
7325                .child(argument_error.child_count().saturating_sub(1))
7326                .is_none_or(|last| last.kind() != ";" || last.is_missing())
7327        {
7328            return false;
7329        }
7330        let mut members: Vec<((usize, usize), String)> = Vec::new();
7331        for index in 0..argument_error.child_count() {
7332            let Some(child) = argument_error.child(index) else {
7333                continue;
7334            };
7335            if child.kind() == "type_descriptor" {
7336                let start = (0..index)
7337                    .rev()
7338                    .find_map(|previous| {
7339                        argument_error
7340                            .child(previous)
7341                            .filter(|sibling| sibling.kind() == "explicit")
7342                    })
7343                    .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7344                let Some(name) = child
7345                    .child_by_field_name("type")
7346                    .and_then(|type_node| direct_identifier_name(type_node, self.source))
7347                else {
7348                    return false;
7349                };
7350                members.push(((start, argument_error.end_byte()), name));
7351            }
7352        }
7353        let [(first_range, first_name)] = members.as_slice() else {
7354            return false;
7355        };
7356        let Some(second) = specifier
7357            .child_by_field_name("type")
7358            .filter(|type_node| type_node.kind() == "type_descriptor")
7359        else {
7360            return false;
7361        };
7362        let Some(second_name) = second
7363            .child_by_field_name("type")
7364            .and_then(|type_node| direct_identifier_name(type_node, self.source))
7365        else {
7366            return false;
7367        };
7368        let members = [
7369            (*first_range, first_name.as_str()),
7370            ((second.start_byte(), node.end_byte()), second_name.as_str()),
7371        ];
7372        let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7373        self.record_recovered_declarations(recovery, node, |visitor| {
7374            for ((start, end), member_name) in members {
7375                let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7376                    continue;
7377                };
7378                let root = tree.root_node();
7379                // The file-scope reparse of a member-shaped head is errorful
7380                // (a member qualifier is not valid there), so the admission is
7381                // the declarator itself: exactly one function declarator
7382                // naming the member inside its own range.
7383                let mut candidate = None;
7384                let mut stack = vec![root];
7385                while let Some(current) = stack.pop() {
7386                    if current.kind() == "function_declarator"
7387                        && current.start_byte() >= start
7388                        && current.end_byte() <= end
7389                        && cpp_function_declarator_name_node(current)
7390                            .is_some_and(|name| node_text(name, visitor.source) == member_name)
7391                    {
7392                        if candidate.is_some() {
7393                            candidate = None;
7394                            break;
7395                        }
7396                        candidate = Some(current);
7397                        continue;
7398                    }
7399                    let mut cursor = current.walk();
7400                    stack.extend(current.named_children(&mut cursor));
7401                }
7402                let Some(declarator) = candidate else {
7403                    continue;
7404                };
7405                let reparsed_ancestry = visitor.recovery_ancestry(root);
7406                visitor.add_macro_wrapped_declaration(
7407                    MacroWrappedDeclaration {
7408                        declarator,
7409                        range: cpp_recovery_window(visitor.source, start, end),
7410                        is_static: false,
7411                    },
7412                    scope,
7413                    &reparsed_ancestry,
7414                );
7415            }
7416        });
7417        true
7418    }
7419
7420    fn add_macro_wrapped_declaration<'tree>(
7421        &mut self,
7422        declaration: MacroWrappedDeclaration<'tree>,
7423        scope: &ScopeInfo,
7424        ancestry: &ParentIndex<'tree>,
7425    ) {
7426        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7427        else {
7428            return;
7429        };
7430        let code_unit =
7431            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7432        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7433        let is_constructor = scope
7434            .class_unit
7435            .as_ref()
7436            .is_some_and(|class| function.name == class.identifier())
7437            || cpp_callable_is_structural_constructor(
7438                declaration.declarator,
7439                self.source,
7440                ancestry,
7441            );
7442        let already_declared = self.parsed.contains_declaration(&code_unit);
7443        if already_declared {
7444            self.parsed
7445                .record_navigation_range(code_unit.clone(), declaration.range);
7446            if definition.is_none() {
7447                return;
7448            }
7449            // Preserve the earlier declaration-only occurrence while adding
7450            // the recovered body as a distinct definition occurrence.
7451        }
7452        self.add_declaration_with_range(
7453            ancestry,
7454            code_unit.clone(),
7455            declaration.range,
7456            Some(declaration.declarator),
7457            None,
7458            None,
7459        );
7460        if definition.is_some() {
7461            let recovered_declaration = *self
7462                .source_links
7463                .get(&code_unit)
7464                .and_then(|links| links.last())
7465                .expect("recovered definition source");
7466            let mut properties = self.source_properties.borrow_mut();
7467            let fact = properties
7468                .get_mut(&recovered_declaration)
7469                .expect("recovered definition source fact");
7470            fact.occurrence_role =
7471                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7472            fact.callable_is_constructor |= is_constructor;
7473        }
7474        let signature = normalize_cpp_whitespace(
7475            self.source
7476                .get(declaration.range.start_byte..declaration.range.end_byte)
7477                .expect("a recovered declaration range covers one source range"),
7478        );
7479        let linkage = if declaration.is_static {
7480            CallableLinkage::Internal
7481        } else {
7482            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7483        };
7484        // Whether this is a definition is a property of the recovered node, not
7485        // of the caller: a declarator that a `function_definition` gives a body
7486        // is a definition wherever the recovery found it.
7487        let declaration_only = definition.is_none();
7488        self.add_signature_with_metadata(
7489            code_unit.clone(),
7490            cpp_signature_metadata(
7491                signature,
7492                declaration.declarator,
7493                linkage,
7494                self.source,
7495                ancestry,
7496            )
7497            .with_declaration_only(declaration_only),
7498        );
7499        if already_declared {
7500            return;
7501        }
7502        if let Some(parent) = &scope.class_unit {
7503            self.parsed.add_child(parent.clone(), code_unit);
7504        } else if let Some(module) = &scope.module {
7505            self.parsed.add_child(module.clone(), code_unit);
7506        }
7507    }
7508
7509    /// Index only anonymous aggregate declarations from an ordinary
7510    /// function body. Function bodies are otherwise outside the declaration
7511    /// walk, but their anonymous aggregate owners must exist so structured
7512    /// local binding inference can name their fields (#2994).
7513    fn visit_anonymous_local_aggregates_in_function<'tree>(
7514        &mut self,
7515        function: Node<'tree>,
7516        scope: &ScopeInfo,
7517        stack: &mut Vec<CppWork<'tree>>,
7518        ancestry: &ParentIndex<'tree>,
7519    ) {
7520        let Some(body) = cpp_body_node(function) else {
7521            return;
7522        };
7523        // A body opens a block scope, so nothing declared in it is a member of
7524        // the class the definition may be written inside. Dropping the
7525        // aggregate carrier gives an inline member function's body the same
7526        // block scope an out-of-line `void C::f()` body already has; without
7527        // it a C++ header, where member bodies are usually inline, would keep
7528        // none of these owners (#3301).
7529        let block_scope;
7530        let scope = if scope.class_unit.is_some() {
7531            block_scope = ScopeInfo {
7532                class_unit: None,
7533                declarations_are_fields: false,
7534                ..scope.clone()
7535            };
7536            &block_scope
7537        } else {
7538            scope
7539        };
7540        let mut pending = vec![body];
7541        while let Some(node) = pending.pop() {
7542            if matches!(node.kind(), "function_definition" | "lambda_expression") {
7543                continue;
7544            }
7545            if node.kind() == "declaration"
7546                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7547            {
7548                continue;
7549            }
7550            if matches!(
7551                node.kind(),
7552                "class_specifier" | "struct_specifier" | "union_specifier"
7553            ) {
7554                continue;
7555            }
7556            let mut cursor = node.walk();
7557            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7558            children.reverse();
7559            pending.extend(children);
7560        }
7561    }
7562
7563    fn visit_error_swallowed_function_declaration<'tree>(
7564        &mut self,
7565        node: Node<'tree>,
7566        scope: &ScopeInfo,
7567    ) -> bool {
7568        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7569            return false;
7570        };
7571        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7572            return false;
7573        };
7574        let root = tree.root_node();
7575        let mut cursor = root.walk();
7576        let declarations = root
7577            .named_children(&mut cursor)
7578            .filter(|child| child.kind() != "comment")
7579            .collect::<Vec<_>>();
7580        let [declaration] = declarations.as_slice() else {
7581            return false;
7582        };
7583        if declaration.kind() != "declaration"
7584            || declaration.has_error()
7585            || declaration.start_byte() != start
7586            || declaration.end_byte() != end
7587        {
7588            return false;
7589        }
7590        let recovery = cpp_recovery_window(self.source, start, end);
7591        // The reparsed region is its own tree, so this walk indexes it itself.
7592        let reparsed_ancestry = self.recovery_ancestry(root);
7593        self.record_recovered_declarations(recovery, node, |visitor| {
7594            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7595        });
7596        true
7597    }
7598
7599    /// Recover a K&R-compatibility prototype macro invocation such as
7600    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
7601    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
7602    /// defined only in headers outside this workspace (ruby's own
7603    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
7604    /// `name _((args))` and turns each such line into a malformed
7605    /// `declaration` or pointer-expression statement. Before this
7606    /// recovery, the ordinary declaration visitor read that wreckage as a
7607    /// field with the type and name swapped (e.g. a field literally named
7608    /// `VALUE`) and indexed no prototype for the real name at all.
7609    ///
7610    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
7611    /// preserve the declared name, a known one-argument compatibility macro,
7612    /// the nested argument list, and a live terminator. It rejects arbitrary
7613    /// identifiers and bare `ERROR` envelopes that flattened those relations;
7614    /// guessing there previously turned malformed non-macro declarations into
7615    /// invented functions. For each proven candidate this reparses the range
7616    /// before the macro token, the inner parenthesized argument node, and the
7617    /// terminating `;` as one included-range parse. That is the parser-native
7618    /// equivalent of expanding `#define _(args) args` while retaining original
7619    /// byte offsets. The result is admitted only when it is exactly one clean
7620    /// function prototype spanning the recovered run.
7621    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7622        for candidate in cpp_prototype_macro_candidates(node, self.source) {
7623            let start = candidate.run_start;
7624            let end = candidate.semicolon_end;
7625            if self.byte_range_is_inside_consumed_fragment(start, end) {
7626                continue;
7627            }
7628            let Some(tree) = parse_source_ranges_with_cancellation(
7629                &tree_sitter_cpp::LANGUAGE.into(),
7630                self.source,
7631                &candidate.ranges(),
7632                None,
7633            ) else {
7634                continue;
7635            };
7636            let root = tree.root_node();
7637            let mut cursor = root.walk();
7638            let declarations = root
7639                .named_children(&mut cursor)
7640                .filter(|child| child.kind() != "comment")
7641                .collect::<Vec<_>>();
7642            let [declaration] = declarations.as_slice() else {
7643                continue;
7644            };
7645            if declaration.kind() != "declaration"
7646                || declaration.has_error()
7647                || declaration.start_byte() != start
7648                || declaration.end_byte() != end
7649                || declaration
7650                    .child_by_field_name("declarator")
7651                    .and_then(extract_function_declarator)
7652                    .is_none()
7653            {
7654                continue;
7655            }
7656            let recovery = cpp_recovery_window(self.source, start, end);
7657            // The reparsed region is its own tree, so this walk indexes it
7658            // itself.
7659            let reparsed_ancestry = self.recovery_ancestry(root);
7660            self.record_recovered_declarations(recovery, node, |visitor| {
7661                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7662            });
7663            self.consumed_fragment_regions.push((start, end));
7664        }
7665    }
7666
7667    /// Declare one Module per namespace level of `levels` under `package_name`,
7668    /// each recording the range of the construct that writes it, and return the
7669    /// innermost level's package name and Module. A level an earlier definition
7670    /// already declared is reused, which is also how a reopened namespace keeps
7671    /// the range of the definition that first declared it.
7672    fn declare_namespace_levels(
7673        &mut self,
7674        mut package_name: String,
7675        levels: Vec<(String, Range, Option<Range>)>,
7676        node: Node<'_>,
7677        ancestry: &ParentIndex<'_>,
7678    ) -> (String, Option<CodeUnit>) {
7679        let mut module = None;
7680        for (component, range, name_range) in levels {
7681            let full_name = if package_name.is_empty() {
7682                component
7683            } else {
7684                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7685            };
7686            let level = CodeUnit::new_fq(
7687                self.file.clone(),
7688                CodeUnitType::Module,
7689                "",
7690                full_name.clone(),
7691                cpp_namespace_fq(&full_name),
7692            );
7693            if !self.parsed.contains_declaration(&level) {
7694                if let Some(name_range) = name_range {
7695                    self.add_declaration_with_name_range(
7696                        level.clone(),
7697                        range,
7698                        name_range,
7699                        Some((ancestry, node)),
7700                        None,
7701                        None,
7702                    );
7703                } else {
7704                    self.add_declaration_with_range(
7705                        ancestry,
7706                        level.clone(),
7707                        range,
7708                        Some(node),
7709                        None,
7710                        None,
7711                    );
7712                }
7713            }
7714            package_name = full_name;
7715            module = Some(level);
7716        }
7717        (package_name, module)
7718    }
7719
7720    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
7721    /// index restores. Each level carries the range of its own name token,
7722    /// which the walk's current node cannot supply: that node is whichever
7723    /// declaration the walk reached first inside the region, and deriving a
7724    /// level's name from it paired the level's range with an unrelated
7725    /// construct's name token (#4292).
7726    fn declare_recovered_namespace_levels(
7727        &mut self,
7728        mut package_name: String,
7729        levels: Vec<(String, Range, Range)>,
7730    ) -> (String, Option<CodeUnit>) {
7731        let mut module = None;
7732        for (component, range, name_range) in levels {
7733            let full_name = if package_name.is_empty() {
7734                component
7735            } else {
7736                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7737            };
7738            let level = CodeUnit::new_fq(
7739                self.file.clone(),
7740                CodeUnitType::Module,
7741                "",
7742                full_name.clone(),
7743                cpp_namespace_fq(&full_name),
7744            );
7745            if !self.parsed.contains_declaration(&level) {
7746                self.add_declaration_with_name_range(
7747                    level.clone(),
7748                    range,
7749                    name_range,
7750                    None,
7751                    None,
7752                    None,
7753                );
7754            }
7755            package_name = full_name;
7756            module = Some(level);
7757        }
7758        (package_name, module)
7759    }
7760
7761    /// Restore the complete lexical namespace path when recovery dropped an
7762    /// ancestor or retained it past its real close. Parsed namespaces beginning
7763    /// inside the recovered region still contribute their normal nesting.
7764    /// Crossing that boundary also clears any malformed class owner.
7765    fn recovered_namespace_scope(
7766        &mut self,
7767        node: Node<'_>,
7768        scope: &ScopeInfo,
7769    ) -> Option<ScopeInfo> {
7770        self.orphaned_namespaces.region_at(node.start_byte())?;
7771        // Each level carries the `namespace Name { ... }` construct that writes
7772        // it along with the level's own name token. Declaring them at `node`
7773        // instead recorded the range of whichever member the walk happened to
7774        // reach first, so a namespace whose head collapsed into an ERROR
7775        // reported its first inner declaration's lines as its own (#3309), and
7776        // deriving the name from that node paired the level's range with the
7777        // member's name token (#4292).
7778        let levels = self
7779            .orphaned_namespaces
7780            .enclosing_namespace_levels(node, self.source);
7781        let package_name = levels
7782            .iter()
7783            .map(|(component, ..)| component.as_str())
7784            .collect::<Vec<_>>()
7785            .join(CPP_PACKAGE_SEPARATOR);
7786        if package_name == scope.package_name {
7787            return None;
7788        }
7789        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7790        Some(ScopeInfo {
7791            package_name,
7792            module,
7793            // A recovered namespace body is no longer inside the class (or
7794            // function) that tree-sitter accidentally used as its wrapper.
7795            // Keep namespace/module context, but never carry that owner into
7796            // declarations published from the recovered region.
7797            class_unit: None,
7798            template_signature: None,
7799            template_metadata: None,
7800            declarations_are_fields: false,
7801            recovered_specialization_member_scope: false,
7802            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7803        })
7804    }
7805
7806    fn visit_namespace<'tree>(
7807        &mut self,
7808        node: Node<'tree>,
7809        scope: &ScopeInfo,
7810        stack: &mut Vec<CppWork<'tree>>,
7811        ancestry: &ParentIndex<'tree>,
7812    ) {
7813        let name_node = node.child_by_field_name("name");
7814        let Some(name_node) = name_node else {
7815            if let Some(body) = cpp_body_node(node) {
7816                stack.push(CppWork::Container(CppContainer {
7817                    node: body,
7818                    scope: scope.clone(),
7819                }));
7820            }
7821            return;
7822        };
7823        // Diagnostic corpora contain deliberately ill-formed global namespace
7824        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
7825        // the leading global `::` as the first anonymous child. Honor that AST
7826        // boundary instead of appending the name to the lexical namespace;
7827        // appending produced legacy names such as `outer::::outer::inner`, which
7828        // could not round-trip through the structured FqName boundary.
7829        let explicitly_global = name_node
7830            .child(0)
7831            .is_some_and(|child| !child.is_named() && child.kind() == "::");
7832        let components = cpp_namespace_name_components(name_node, self.source);
7833        if components.is_empty() {
7834            return;
7835        }
7836        // One Module per namespace level. C++17's `namespace a::b { ... }` is
7837        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
7838        // shorthand must declare `a` as well as `a::b` -- extracting only the
7839        // innermost level left the enclosing namespace undeclared and made the
7840        // two spellings of one construct disagree (issue #1878).
7841        let package_name = if explicitly_global {
7842            String::new()
7843        } else {
7844            scope.package_name.clone()
7845        };
7846        let definition = node_range(node);
7847        // The `namespace a::b` shorthand names every level in one node: each
7848        // level's name occurrence is its own token, the same evidence the
7849        // recovered-namespace path carries per level (#4292).
7850        let mut component_ranges =
7851            cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
7852        let (package_name, module) = self.declare_namespace_levels(
7853            package_name,
7854            components
7855                .into_iter()
7856                .map(|component| {
7857                    let name_range = component_ranges
7858                        .next()
7859                        .filter(|(name, _)| *name == component)
7860                        .map(|(_, range)| range);
7861                    (component, definition, name_range)
7862                })
7863                .collect(),
7864            node,
7865            ancestry,
7866        );
7867
7868        let namespace_scope = ScopeInfo {
7869            package_name,
7870            module,
7871            // C++ never nests a namespace inside a class, so a surviving
7872            // class_unit here is always recovery bleed: a malformed-region
7873            // boundary upstream mis-scoped this namespace block. Keeping the
7874            // owner would mint the namespace's declarations as class members
7875            // under a re-appended package, desyncing the fq boundary assert
7876            // (#2306). Dropping it is identity-neutral for valid code, where
7877            // class_unit is always empty at a namespace definition.
7878            class_unit: None,
7879            template_signature: scope.template_signature.clone(),
7880            template_metadata: scope.template_metadata.clone(),
7881            declarations_are_fields: false,
7882            recovered_specialization_member_scope: false,
7883            visible_using_namespaces: scope.visible_using_namespaces.clone(),
7884        };
7885        let container = cpp_body_node(node).unwrap_or(node);
7886        // A malformed export-macro class body may turn the following class
7887        // into a descendant of a bogus function/labeled/error envelope. Those
7888        // descendants are not declaration containers and the ordinary walk
7889        // intentionally does not descend into them. Scan the namespace tree
7890        // once for the strict embedded class geometry before scheduling its
7891        // normal declarations. When one envelope matches, its helper recovers
7892        // every embedded class and the walk need not inspect its descendants.
7893        let mut candidates = vec![container];
7894        while let Some(candidate) = candidates.pop() {
7895            if matches!(
7896                candidate.kind(),
7897                "ERROR" | "function_definition" | "labeled_statement"
7898            ) && self.visit_embedded_function_like_export_classes(
7899                candidate,
7900                &namespace_scope,
7901                stack,
7902                ancestry,
7903            ) {
7904                continue;
7905            }
7906            for index in (0..candidate.named_child_count()).rev() {
7907                candidates.push(
7908                    candidate
7909                        .named_child(index)
7910                        .expect("index below the node's own named child count"),
7911                );
7912            }
7913        }
7914        stack.push(CppWork::Container(CppContainer {
7915            node: container,
7916            scope: namespace_scope,
7917        }));
7918    }
7919
7920    fn visit_class_like<'tree>(
7921        &mut self,
7922        node: Node<'tree>,
7923        scope: &ScopeInfo,
7924        stack: &mut Vec<CppWork<'tree>>,
7925        ancestry: &ParentIndex<'tree>,
7926    ) {
7927        let Some(name) = class_like_name(node, self.source, ancestry) else {
7928            return;
7929        };
7930        let name = qualified_class_name_chain(node, self.source, scope)
7931            .map(|chain| chain.join("$"))
7932            .unwrap_or(name);
7933        self.visit_named_class_like(node, name, scope, stack, ancestry);
7934    }
7935
7936    fn visit_named_class_like<'tree>(
7937        &mut self,
7938        node: Node<'tree>,
7939        name: String,
7940        scope: &ScopeInfo,
7941        stack: &mut Vec<CppWork<'tree>>,
7942        ancestry: &ParentIndex<'tree>,
7943    ) {
7944        let body = cpp_body_node(node);
7945        let definition_body_present = body.is_some();
7946        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
7947            .then(|| extract_cpp_supertypes(node, self.source));
7948        self.visit_named_class_like_shape(
7949            node,
7950            name,
7951            body,
7952            definition_body_present,
7953            None,
7954            raw_supertypes,
7955            Some(extract_cpp_base_facts(node, self.source)),
7956            scope,
7957            stack,
7958            ancestry,
7959        );
7960    }
7961
7962    /// Whether this class-like declaration is a C tag that belongs to the
7963    /// enclosing non-aggregate scope rather than to the aggregate it is
7964    /// lexically written inside.
7965    ///
7966    /// `class_specifier` is deliberately excluded: `class` is not C, so text
7967    /// that spells one in a `.c` file is not C code and keeps the C++ reading
7968    /// rather than getting a half-C identity.
7969    fn mints_tag_at_enclosing_c_scope(
7970        &self,
7971        declaration_node: Node<'_>,
7972        scope: &ScopeInfo,
7973        ancestry: &ParentIndex<'_>,
7974    ) -> bool {
7975        if scope.class_unit.is_none()
7976            || !matches!(
7977                declaration_node.kind(),
7978                "struct_specifier" | "union_specifier" | "enum_specifier"
7979            )
7980            || class_like_name(declaration_node, self.source, ancestry).is_none()
7981        {
7982            return false;
7983        }
7984        // A class-specifier ancestor proves this is C++ syntax. A C reading of
7985        // the header must not apply C tag hoisting to structs nested in that
7986        // class, because that invents a file-scope identity for its members.
7987        let mut ancestor = ancestry.parent(declaration_node);
7988        while let Some(current) = ancestor {
7989            if current.kind() == "class_specifier" {
7990                return false;
7991            }
7992            ancestor = ancestry.parent(current);
7993        }
7994        true
7995    }
7996
7997    #[allow(clippy::too_many_arguments)]
7998    fn visit_named_class_like_shape<'tree>(
7999        &mut self,
8000        declaration_node: Node<'tree>,
8001        name: String,
8002        body: Option<Node<'tree>>,
8003        definition_body_present: bool,
8004        explicit_range: Option<Range>,
8005        raw_supertypes: Option<Vec<String>>,
8006        base_facts: Option<Vec<CppBaseSpecifierFact>>,
8007        scope: &ScopeInfo,
8008        stack: &mut Vec<CppWork<'tree>>,
8009        ancestry: &ParentIndex<'tree>,
8010    ) -> CodeUnit {
8011        let displaced_macro_tail = if explicit_range.is_none() {
8012            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8013        } else {
8014            None
8015        };
8016        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8017        let recovered_scope = self.scope_for_recovered_exported_class(
8018            declaration_node,
8019            &name,
8020            definition_body_present,
8021            scope,
8022            ancestry,
8023        );
8024        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
8025        // aggregate's member list is declared at the enclosing non-aggregate
8026        // scope, not nested inside the aggregate. `scope.class_unit` is the
8027        // only aggregate carrier in this walk, so dropping it puts the tag at
8028        // the nearest enclosing non-aggregate scope -- the module at file or
8029        // namespace scope, and the same block-scope representation a
8030        // function-local aggregate already gets. The tag's own body scope
8031        // below still owns its members, so fields and enumerators are
8032        // unaffected.
8033        let c_tag_scope;
8034        let c_tag_scope_applies =
8035            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8036        if !self.c_tag_semantics && c_tag_scope_applies {
8037            self.c_tag_scope_witness = true;
8038        }
8039        let scope = if self.c_tag_semantics && c_tag_scope_applies {
8040            c_tag_scope = ScopeInfo {
8041                class_unit: None,
8042                ..recovered_scope.clone()
8043            };
8044            &c_tag_scope
8045        } else {
8046            &recovered_scope
8047        };
8048        let short_name = if let Some(parent) = &scope.class_unit {
8049            cpp_join_nested_short(parent.short_name(), &name)
8050        } else {
8051            name.clone()
8052        };
8053        // A top-level out-of-line qualified class definition (`struct
8054        // Outer::Inner { ... }` inside its namespace, #2246) carries its
8055        // nesting chain as the `$`-joined display name; push one Type/Nested
8056        // segment per class so segment-pop owner navigation keeps working.
8057        // Every other leaf name stays opaque so a literal `$` in a source
8058        // identifier never crosses the split/join boundary (#2140).
8059        let qualified_chain = if scope.class_unit.is_none() {
8060            qualified_class_name_chain(declaration_node, self.source, scope)
8061                .filter(|chain| chain.join("$") == name)
8062        } else {
8063            None
8064        };
8065        let fq = if let Some(chain) = qualified_chain {
8066            let mut fq = FqName::new();
8067            cpp_push_package(&mut fq, &scope.package_name);
8068            let mut first = true;
8069            for component in chain {
8070                let kind = if first {
8071                    SegmentKind::Type
8072                } else {
8073                    SegmentKind::Nested
8074                };
8075                fq.push(segment_interner().intern(&component, kind));
8076                first = false;
8077            }
8078            fq
8079        } else {
8080            cpp_leaf_fq(
8081                &scope.package_name,
8082                scope.class_unit.as_ref(),
8083                &name,
8084                SegmentKind::Nested,
8085                SegmentKind::Type,
8086            )
8087        };
8088        let code_unit = CodeUnit::with_signature_and_fq(
8089            self.file.clone(),
8090            CodeUnitType::Class,
8091            scope.package_name.clone(),
8092            short_name,
8093            scope.template_signature.clone(),
8094            false,
8095            fq,
8096        );
8097        let has_body = definition_body_present;
8098        if !has_body && self.parsed.contains_declaration(&code_unit) {
8099            if let Some(range) = explicit_range {
8100                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8101            } else {
8102                self.link_source_declaration(ancestry, &code_unit, declaration_node);
8103            }
8104            self.parsed.record_navigation_range(
8105                code_unit.clone(),
8106                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8107            );
8108            return code_unit;
8109        }
8110        if has_body {
8111            // A forward declaration above this definition is one of the
8112            // class's declaration sites: C++ injects the class name at the
8113            // forward declaration, so a reference between the two spells this
8114            // class (#3297). Moving the unit to its body must not drop the
8115            // earlier site, exactly as a function prototype and its body stay
8116            // two ranges of one declaration (#1650).
8117            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8118            if let Some(range) = explicit_range {
8119                self.replace_declaration_with_range_deferred(
8120                    ancestry,
8121                    code_unit.clone(),
8122                    range,
8123                    Some(declaration_node),
8124                    None,
8125                    None,
8126                );
8127            } else {
8128                self.replace_declaration_deferred(
8129                    ancestry,
8130                    code_unit.clone(),
8131                    declaration_node,
8132                    None,
8133                    None,
8134                );
8135            }
8136            for range in earlier_ranges {
8137                self.parsed.add_declaration_range(&code_unit, range);
8138            }
8139        } else {
8140            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8141        }
8142        if let Some(declaration) = self
8143            .source_links
8144            .get(&code_unit)
8145            .and_then(|links| links.last())
8146            .copied()
8147        {
8148            let mut properties = self.source_properties.borrow_mut();
8149            let fact = properties
8150                .get_mut(&declaration)
8151                .expect("linked class has source properties");
8152            fact.occurrence_role = if has_body {
8153                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8154            } else {
8155                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8156            };
8157            fact.class_strength = if has_body {
8158                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8159            } else {
8160                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8161            };
8162            if let Some(base_facts) = base_facts.as_ref() {
8163                fact.bases = base_facts.clone();
8164            }
8165        }
8166        if let Some(raw_supertypes) = raw_supertypes {
8167            self.parsed
8168                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8169        }
8170        self.parsed.add_signature(
8171            code_unit.clone(),
8172            render_cpp_type_signature(
8173                declaration_node,
8174                self.source,
8175                scope.template_signature.as_deref(),
8176            ),
8177        );
8178        if let Some(metadata) = &scope.template_metadata {
8179            let primary_short_name = if let Some(parent) = &scope.class_unit {
8180                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8181            } else {
8182                metadata.primary_name.clone()
8183            };
8184            let primary_fq_name = CodeUnit::new(
8185                self.file.clone(),
8186                CodeUnitType::Class,
8187                scope.package_name.clone(),
8188                primary_short_name,
8189            )
8190            .fq_name();
8191            let mut metadata = metadata.clone();
8192            metadata.primary_fq_name = primary_fq_name;
8193            self.parsed
8194                .set_cpp_template_metadata(code_unit.clone(), metadata);
8195        }
8196        if let Some(parent) = &scope.class_unit {
8197            self.parsed.add_child(parent.clone(), code_unit.clone());
8198        } else if let Some(module) = &scope.module {
8199            self.parsed.add_child(module.clone(), code_unit.clone());
8200        }
8201
8202        if let Some(body) = body {
8203            let mut nested_scope = scope.clone();
8204            nested_scope.class_unit = Some(code_unit.clone());
8205            nested_scope.template_signature = scope.template_signature.clone();
8206            // Template metadata describes the class just created. It must not
8207            // leak into ordinary nested declarations in that class's body.
8208            // Recovered export-macro specializations carry a separate scope bit
8209            // for their declaration-shaped body members.
8210            nested_scope.template_metadata = None;
8211            // Export-macro class bodies recovered from a function_definition use
8212            // compound_statement children, whose direct fields are declarations.
8213            nested_scope.recovered_specialization_member_scope =
8214                scope.template_metadata.as_ref().is_some_and(|metadata| {
8215                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
8216                });
8217            nested_scope.declarations_are_fields =
8218                is_recovered_exported_class_container(declaration_node, self.source)
8219                    || nested_scope.recovered_specialization_member_scope;
8220            if let Some(displaced) = displaced_macro_tail {
8221                // A macro-shaped field without a source semicolon can make
8222                // tree-sitter consume the real class terminator as an ERROR
8223                // inside that field, then retain following namespace items as
8224                // later field-list children. Drain the proven class prefix
8225                // first and re-own only the structured tail with the outer
8226                // scope. The tail is pushed first because the work stack is
8227                // LIFO.
8228                push_cpp_sibling_range(
8229                    body,
8230                    displaced.split_index,
8231                    usize::MAX,
8232                    scope.clone(),
8233                    stack,
8234                );
8235                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8236            } else {
8237                stack.push(CppWork::Container(CppContainer {
8238                    node: body,
8239                    scope: nested_scope,
8240                }));
8241            }
8242        }
8243        if declaration_node.kind() == "enum_specifier" {
8244            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8245            if !self.has_enum_enumerator_units(&code_unit) {
8246                self.visit_enum_enumerators_from_text(
8247                    ancestry,
8248                    declaration_node,
8249                    scope,
8250                    &code_unit,
8251                );
8252            }
8253        }
8254        code_unit
8255    }
8256
8257    /// Whether the parse product already holds enumerator fields for `parent`,
8258    /// answered from the walk's carried-forward field ownership index.
8259    ///
8260    /// Built on the first enum's question and advanced by every declaration
8261    /// recorded after it, so a file that declares no enum -- most files -- pays
8262    /// nothing, and one that declares thousands pays a single pass instead of
8263    /// one per enum (#2786).
8264    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
8265        if self.field_owners.is_none() {
8266            self.field_owners = Some(CppFieldOwnerIndex::of(
8267                self.parsed.declarations().iter(),
8268                self.file,
8269            ));
8270        }
8271        debug_assert_eq!(
8272            parent.source(),
8273            self.file,
8274            "the walk's declarations are declarations of the file it is walking"
8275        );
8276        let carried = self
8277            .field_owners
8278            .as_ref()
8279            .expect("the index was just ensured")
8280            .owns_fields(parent.package_name(), parent.short_name());
8281
8282        #[cfg(debug_assertions)]
8283        assert_eq!(
8284            carried,
8285            cpp_declarations_hold_owned_fields(
8286                self.parsed.declarations(),
8287                self.file,
8288                parent.package_name(),
8289                parent.short_name()
8290            ),
8291            "the carried-forward field index must answer what a fresh declaration scan \
8292             answers for {}",
8293            parent.fq_name()
8294        );
8295
8296        carried
8297    }
8298
8299    fn visit_enum_enumerators(
8300        &mut self,
8301        ancestry: &ParentIndex<'_>,
8302        node: Node<'_>,
8303        scope: &ScopeInfo,
8304        parent: &CodeUnit,
8305    ) {
8306        walk_named_tree_preorder(node, false, |child| {
8307            if child.kind() != "enumerator" {
8308                return WalkControl::Continue;
8309            }
8310            let Some(name_node) = child.child_by_field_name("name") else {
8311                return WalkControl::Continue;
8312            };
8313            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8314            if name.is_empty() {
8315                return WalkControl::Continue;
8316            }
8317            let signature = normalize_cpp_whitespace(node_text(child, self.source));
8318            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8319            WalkControl::Continue
8320        });
8321    }
8322
8323    fn visit_enum_enumerator(
8324        &mut self,
8325        ancestry: &ParentIndex<'_>,
8326        declaration_node: Node<'_>,
8327        scope: &ScopeInfo,
8328        parent: &CodeUnit,
8329        name: &str,
8330        signature: &str,
8331    ) {
8332        let code_unit = CodeUnit::new_fq(
8333            self.file.clone(),
8334            CodeUnitType::Field,
8335            scope.package_name.clone(),
8336            cpp_join_member_short(parent.short_name(), name),
8337            parent
8338                .fq()
8339                .clone()
8340                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8341        );
8342        if self.parsed.contains_declaration(&code_unit) {
8343            return;
8344        }
8345        self.add_declaration(
8346            ancestry,
8347            code_unit.clone(),
8348            declaration_node,
8349            Some(parent.clone()),
8350            None,
8351        );
8352        self.parsed.add_signature(code_unit, signature.to_string());
8353    }
8354
8355    fn visit_enum_enumerators_from_text(
8356        &mut self,
8357        ancestry: &ParentIndex<'_>,
8358        node: Node<'_>,
8359        scope: &ScopeInfo,
8360        parent: &CodeUnit,
8361    ) {
8362        let text = node_text(node, self.source);
8363        let Some((_, body)) = text.split_once('{') else {
8364            return;
8365        };
8366        let Some((body, _)) = body.rsplit_once('}') else {
8367            return;
8368        };
8369        for entry in body.split(',') {
8370            let trimmed = entry.trim();
8371            let name = trimmed
8372                .split('=')
8373                .next()
8374                .unwrap_or("")
8375                .split_whitespace()
8376                .next()
8377                .unwrap_or("");
8378            if name.is_empty() {
8379                continue;
8380            }
8381            self.visit_enum_enumerator(ancestry, node, scope, parent, name, trimmed);
8382        }
8383    }
8384
8385    fn visit_function_definition<'tree>(
8386        &mut self,
8387        node: Node<'tree>,
8388        scope: &ScopeInfo,
8389        stack: &mut Vec<CppWork<'tree>>,
8390        ancestry: &ParentIndex<'tree>,
8391    ) {
8392        // An attribute-like macro invocation whose argument is a declaration can
8393        // swallow every declaration written after it into one bogus
8394        // `function_definition` (#2551). This owns the whole region, so it runs
8395        // ahead of `visit_macro_swallowed_function_declarations` below, which
8396        // admits the same envelope by its head macro token and reads single
8397        // declarators out of nodes this recovery reparses properly.
8398        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8399            return;
8400        }
8401        // A file-scope object-like macro sentinel the parser cannot see (issue
8402        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
8403        // prefixes as a bogus `function_definition` that swallows real namespaces,
8404        // classes, and members. Reparse the swallowed interior as C++ items so the
8405        // ordinary declaration visitors index it with byte/line-exact ownership.
8406        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8407            return;
8408        }
8409        // The omitted-semicolon field near miss (`DWORD preserved_field` ahead
8410        // of `void PreserveFollowingFunction() {}`): the macro-declaration
8411        // grammar folds the pair into the function's head as two leading
8412        // macro_modifiers where the older grammar left a damaged
8413        // field_declaration. Index the field; the function itself continues
8414        // through the ordinary path below.
8415        if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8416            self.visit_omitted_semicolon_macro_field(
8417                node,
8418                type_modifier,
8419                name_modifier,
8420                scope,
8421                ancestry,
8422            );
8423        }
8424        // The macro-return stand-in (`CUTLASS_DEVICE static OperandLayout
8425        // Layout() { ... }`): the same clean envelope, but the return type
8426        // names an active template type parameter, and the older grammar's
8427        // phantom field was the definition `OperandLayout::packed` resolved to
8428        // (#1129). Recover that field from the clean head.
8429        if scope.class_unit.is_some()
8430            && let Some((head_start, type_node)) =
8431                macro_return_template_parameter_field(node, self.source, ancestry)
8432        {
8433            self.visit_macro_return_template_parameter_field(
8434                node, head_start, type_node, scope, ancestry,
8435            );
8436        }
8437        if node.has_error() {
8438            self.visit_macro_swallowed_function_declarations(node, scope);
8439        }
8440        if let Some((class_node, name, raw_supertypes, base_facts)) =
8441            recover_exported_class_function_definition(node, self.source)
8442        {
8443            if let Some(body) = cpp_body_node(node)
8444                && let Some(close) = self
8445                    .orphaned_namespaces
8446                    .matching_close_brace(body.start_byte())
8447                && close.end_byte < body.end_byte()
8448            {
8449                let class_range = Range {
8450                    start_byte: node.start_byte(),
8451                    end_byte: close.end_byte,
8452                    start_line: node.start_position().row + 1,
8453                    end_line: close.end_line,
8454                };
8455                // Parse the body in class context, preserving the original
8456                // keyword, recovered name and body as included ranges. Parsing
8457                // the interior at file scope turns constructors and access
8458                // labels into another oversized ERROR instead of members.
8459                let mut head = vec![node];
8460                let mut keyword = None;
8461                let mut name_node = None;
8462                while let Some(part) = head.pop() {
8463                    if part.start_byte() >= body.start_byte() || part.is_missing() {
8464                        continue;
8465                    }
8466                    if matches!(part.kind(), "class" | "struct" | "union") {
8467                        keyword = Some(part);
8468                    }
8469                    if matches!(part.kind(), "identifier" | "type_identifier")
8470                        && node_text(part, self.source) == name
8471                    {
8472                        name_node = Some(part);
8473                    }
8474                    let mut cursor = part.walk();
8475                    head.extend(part.children(&mut cursor));
8476                }
8477                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8478                    && let Some(type_name) = keyword
8479                        .parent()
8480                        .and_then(|parent| parent.child_by_field_name("name"))
8481                    && let Some(tree) = parse_source_ranges_with_cancellation(
8482                        &tree_sitter_cpp::LANGUAGE.into(),
8483                        self.source,
8484                        &[
8485                            (keyword.start_byte(), type_name.start_byte()),
8486                            (name_node.start_byte(), name_node.end_byte()),
8487                            (body.start_byte(), close.end_byte),
8488                        ],
8489                        None,
8490                    )
8491                    && let Some(reparsed_class) = tree.root_node().named_child(0)
8492                    && let Some(class_body) = cpp_body_node(reparsed_class)
8493                    && class_body.start_byte() == body.start_byte()
8494                    && class_body.end_byte() == close.end_byte
8495                    && let Some(tail) =
8496                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8497                {
8498                    let class_body_range = class_body.byte_range();
8499                    self.with_recovery_source_context(node, |visitor| {
8500                        let source_context = visitor.active_source_context.clone();
8501                        let class_unit = visitor.visit_named_class_like_shape(
8502                            class_node,
8503                            name,
8504                            None,
8505                            true,
8506                            Some(class_range),
8507                            raw_supertypes,
8508                            base_facts,
8509                            scope,
8510                            stack,
8511                            ancestry,
8512                        );
8513                        visitor.parsed.record_materialization(
8514                            MaterializationRecord::RecoveredDeclaration {
8515                                recovery: class_range,
8516                                unit: class_unit.clone(),
8517                            },
8518                        );
8519                        let member_scope = ScopeInfo {
8520                            package_name: class_unit.package_name().to_string(),
8521                            class_unit: Some(class_unit),
8522                            declarations_are_fields: true,
8523                            template_metadata: None,
8524                            recovered_specialization_member_scope: false,
8525                            ..scope.clone()
8526                        };
8527                        let tail_range = tail.root_node().byte_range();
8528                        let class_body_recovery = cpp_recovery_window(
8529                            visitor.source,
8530                            class_body_range.start,
8531                            class_body_range.end,
8532                        );
8533                        let tail_recovery =
8534                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8535                        visitor.partitioned_regions.push(CppPartitionedRegion {
8536                            tree: tail,
8537                            range: tail_range,
8538                            scope: scope.clone(),
8539                            recovery: tail_recovery,
8540                            source_context: source_context.clone(),
8541                        });
8542                        visitor.partitioned_regions.push(CppPartitionedRegion {
8543                            tree,
8544                            range: class_body_range,
8545                            scope: member_scope,
8546                            recovery: class_body_recovery,
8547                            source_context: source_context.clone(),
8548                        });
8549                    });
8550                    return;
8551                }
8552            }
8553            let body = cpp_body_node(class_node);
8554            let displaced_namespace = cpp_body_node(node)
8555                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8556            let fragmented = cpp_body_node(node).and_then(|body| {
8557                fragmented_export_function_body_region(
8558                    node,
8559                    body,
8560                    self.source,
8561                    displaced_namespace.as_ref(),
8562                )
8563            });
8564            // The recovery tuple's first node is the class-like type when the
8565            // parser exposes one, but the synthetic wrapper owns the compound
8566            // statement that contains the truncated class body. Use the
8567            // wrapper body for fragmented-member detection; retain the
8568            // class-node body for the ordinary (non-fragmented) path below.
8569            if let Some(fragmented) = fragmented {
8570                // The lifted sibling no longer sits below the parser-visible
8571                // namespace node. Restore the current parent scope when the
8572                // ordinary work walk reaches that class.
8573                if let Some(boundary) =
8574                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8575                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8576                {
8577                    let mut boundary_scope = scope.clone();
8578                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8579                        if sibling.start_byte() >= boundary.start_byte() {
8580                            break;
8581                        }
8582                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8583                            boundary_scope.visible_using_namespaces.push(namespace);
8584                        }
8585                    }
8586                    self.recovered_class_sibling_scopes
8587                        .insert(boundary.id(), boundary_scope);
8588                }
8589                let mut recovered_constructor = None;
8590                let mut recovered_prefix_tree = None;
8591                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8592                {
8593                    Some(FragmentedExportMembers::Complete(tree)) => {
8594                        if let Some(body) = body
8595                            && let Some(range) =
8596                                cpp_reparsed_synthetic_initializer_constructor_range(
8597                                    tree.root_node(),
8598                                    &name,
8599                                    self.source,
8600                                    body.end_byte(),
8601                                )
8602                        {
8603                            recovered_constructor = Some(range);
8604                            recovered_prefix_tree = Some(tree);
8605                            None
8606                        } else {
8607                            Some(FragmentedExportMembers::Complete(tree))
8608                        }
8609                    }
8610                    outcome => outcome,
8611                };
8612                let mut class_stack = Vec::new();
8613                let class_unit = self.visit_named_class_like_shape(
8614                    class_node,
8615                    name,
8616                    None,
8617                    true,
8618                    Some(fragmented.class_range),
8619                    raw_supertypes,
8620                    base_facts.clone(),
8621                    scope,
8622                    &mut class_stack,
8623                    ancestry,
8624                );
8625                self.parsed
8626                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8627                        recovery: fragmented.class_range,
8628                        unit: class_unit.clone(),
8629                    });
8630                let complete = outcome.is_some_and(|outcome| {
8631                    self.visit_fragmented_export_class_members(
8632                        node,
8633                        outcome,
8634                        class_unit.clone(),
8635                        scope,
8636                    )
8637                });
8638                if complete {
8639                    self.consumed_fragment_regions
8640                        .push((node.start_byte(), fragmented.class_range.end_byte));
8641                } else {
8642                    // The reparse can fail when the first constructor or a
8643                    // method body is split into statement-shaped siblings.
8644                    // Keep the recovered class envelope, but do not visit the
8645                    // synthetic wrapper body: its initializer expressions can
8646                    // look like same-named member functions (for example
8647                    // `Token.location(loc)`). Re-own only the original sibling
8648                    // nodes that fall inside the proven class range. Their CST
8649                    // shapes retain the real field/function kinds and ranges.
8650                    let member_scope = ScopeInfo {
8651                        package_name: class_unit.package_name().to_string(),
8652                        module: scope.module.clone(),
8653                        class_unit: Some(class_unit.clone()),
8654                        template_signature: scope.template_signature.clone(),
8655                        template_metadata: None,
8656                        declarations_are_fields: true,
8657                        recovered_specialization_member_scope: false,
8658                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
8659                    };
8660                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8661                        if candidate.start_byte() >= fragmented.reparse_end {
8662                            break;
8663                        }
8664                        if cpp_fragment_sibling_is_class_member(
8665                            candidate,
8666                            fragmented.reparse_end,
8667                            self.source,
8668                        ) {
8669                            self.recovered_class_sibling_scopes
8670                                .insert(candidate.id(), member_scope.clone());
8671                        }
8672                    }
8673                    if let Some(range) = recovered_constructor
8674                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8675                    {
8676                        let prefix_root = prefix_tree.root_node();
8677                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
8678                        self.visit_recovered_fragment_prefix_members(
8679                            node,
8680                            prefix_root,
8681                            range.start,
8682                            &class_unit,
8683                            scope,
8684                            &prefix_ancestry,
8685                        );
8686                        self.visit_recovered_fragment_constructor(
8687                            range,
8688                            body,
8689                            class_node,
8690                            &class_unit,
8691                            scope,
8692                            ancestry,
8693                        );
8694                    }
8695                }
8696                if let Some(boundary) = displaced_namespace {
8697                    for item in boundary.namespace_items {
8698                        self.recovered_class_sibling_scopes
8699                            .insert(item.id(), scope.clone());
8700                    }
8701                }
8702                stack.extend(class_stack);
8703                return;
8704            }
8705            let mut stack = Vec::new();
8706            let class_unit = self.visit_named_class_like_shape(
8707                class_node,
8708                name,
8709                body,
8710                body.is_some(),
8711                None,
8712                raw_supertypes,
8713                base_facts,
8714                scope,
8715                &mut stack,
8716                ancestry,
8717            );
8718            self.parsed
8719                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8720                    recovery: cpp_declaration_range(node),
8721                    unit: class_unit,
8722                });
8723            // Issue #1524: the bogus `function_definition` body can run past
8724            // the class's true closing brace (the parse ends it with a
8725            // zero-width `MISSING "}"`), swallowing following namespace-scope
8726            // siblings -- they would index as members of the recovered class.
8727            // When the body's text-balanced close lands before the body's own
8728            // end, re-own the swallowed tail with the outer scope instead.
8729            if let Some(body) = body
8730                && let Some(class_close) = self
8731                    .orphaned_namespaces
8732                    .matching_close_brace(body.start_byte())
8733                && class_close.start_byte < body.end_byte()
8734            {
8735                let split = {
8736                    let mut cursor = body.walk();
8737                    body.named_children(&mut cursor)
8738                        .position(|child| child.start_byte() > class_close.start_byte)
8739                };
8740                if let Some(split) = split {
8741                    // The seeded work is a single Container over the whole
8742                    // body with the class scope; replace it with the bounded
8743                    // head (class scope) plus the swallowed tail (outer
8744                    // scope). Push tail first so the head drains first.
8745                    let seeded = stack.pop();
8746                    match seeded {
8747                        Some(CppWork::Container(container)) => {
8748                            push_cpp_sibling_range(
8749                                body,
8750                                split,
8751                                usize::MAX,
8752                                scope.clone(),
8753                                &mut stack,
8754                            );
8755                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8756                        }
8757                        // visit_named_class_like_shape always seeds exactly
8758                        // one Container when a body is present.
8759                        _ => unreachable!("exported-class seed is always one Container"),
8760                    }
8761                }
8762            }
8763            while let Some(work) = stack.pop() {
8764                match work {
8765                    CppWork::Container(container) => {
8766                        push_cpp_container_work(container.node, container.scope, &mut stack);
8767                    }
8768                    CppWork::Siblings(siblings) => {
8769                        advance_cpp_siblings(siblings, self.source, &mut stack);
8770                    }
8771                    CppWork::Node(work) => {
8772                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8773                    }
8774                }
8775            }
8776            return;
8777        }
8778        let recovered_constraint_constructor =
8779            cpp_recovered_template_macro_constructor(node, self.source);
8780        let declarator = recovered_constraint_constructor
8781            .map(|(declarator, _)| declarator)
8782            .or_else(|| {
8783                scope.class_unit.as_ref().and_then(|class| {
8784                    recovered_macro_decorated_constructor_declarator(
8785                        node,
8786                        class.identifier(),
8787                        self.source,
8788                    )
8789                })
8790            })
8791            .or_else(|| node.child_by_field_name("declarator"));
8792        let Some(declarator) = declarator else {
8793            self.visit_malformed_function_definition_container(node, scope, stack);
8794            return;
8795        };
8796        let Some(function_declarator) = extract_function_declarator(declarator) else {
8797            self.visit_malformed_function_definition_container(node, scope, stack);
8798            return;
8799        };
8800        let function = if let Some((_, callable_name)) =
8801            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8802        {
8803            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8804        } else {
8805            extract_function_info(function_declarator, self.source, scope)
8806        };
8807        let Some(mut function) = function else {
8808            self.visit_malformed_function_definition_container(node, scope, stack);
8809            return;
8810        };
8811        if let Some((_, template_parameter)) = recovered_constraint_constructor {
8812            function.signature = format!(
8813                "template <{}>{}",
8814                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8815                function.signature
8816            );
8817        }
8818        let code_unit = function.code_unit(self.file.clone());
8819        // Keep an earlier same-file prototype as another physical occurrence
8820        // of this callable. `CodeUnit` already identifies the role-neutral
8821        // overload, while ranges and signature metadata describe its
8822        // declaration/definition occurrences.
8823        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8824        let signature = if recovered_constraint_constructor.is_some() {
8825            normalize_cpp_whitespace(node_text(function_declarator, self.source))
8826        } else {
8827            render_cpp_function_display_signature_from_node(
8828                node,
8829                self.source,
8830                scope.template_signature.as_deref(),
8831                true,
8832                ancestry,
8833            )
8834        };
8835        self.add_signature_with_metadata(
8836            code_unit.clone(),
8837            cpp_signature_metadata(
8838                signature,
8839                function_declarator,
8840                cpp_callable_linkage(node, self.source, ancestry),
8841                self.source,
8842                ancestry,
8843            )
8844            .with_declaration_only(false),
8845        );
8846        if let Some(parent) = &scope.class_unit {
8847            self.parsed.add_child(parent.clone(), code_unit);
8848        } else if let Some(module) = &scope.module {
8849            self.parsed.add_child(module.clone(), code_unit);
8850        }
8851        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
8852    }
8853
8854    /// Recover the namespace lost when tree-sitter promotes an export-macro
8855    /// class definition to a root-level `function_definition`.  Only a
8856    /// body-bearing, top-level recovery may borrow a namespace, and only when
8857    /// one earlier namespace-scope forward declaration proves the identity.
8858    fn scope_for_recovered_exported_class<'tree>(
8859        &mut self,
8860        node: Node<'tree>,
8861        name: &str,
8862        definition_body_present: bool,
8863        scope: &ScopeInfo,
8864        ancestry: &ParentIndex<'tree>,
8865    ) -> ScopeInfo {
8866        if !definition_body_present
8867            || !scope.package_name.is_empty()
8868            || scope.class_unit.is_some()
8869            || !(is_recovered_exported_class_container(node, self.source)
8870                || recover_function_like_export_class_pair(node, self.source).is_some()
8871                || recover_embedded_function_like_export_classes(node, self.source)
8872                    .iter()
8873                    .any(|recovered| recovered.name == name)
8874                || matches!(node.kind(), "declaration" | "field_declaration")
8875                    && recover_exported_class_declaration(node, self.source).is_some()
8876                || matches!(
8877                    node.kind(),
8878                    "class_specifier" | "struct_specifier" | "union_specifier"
8879                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
8880                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
8881                        name_node,
8882                        self.source,
8883                    )))
8884                }) || ancestry.parent(node).is_some_and(|parent| {
8885                    matches!(parent.kind(), "declaration" | "field_declaration")
8886                        && recover_exported_class_declaration(parent, self.source).is_some()
8887                        || is_recovered_exported_class_container(parent, self.source)
8888                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
8889        {
8890            return scope.clone();
8891        }
8892        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
8893        let Some(package_name) = borrowed_namespace
8894            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
8895        else {
8896            return scope.clone();
8897        };
8898
8899        let module = CodeUnit::new_fq(
8900            self.file.clone(),
8901            CodeUnitType::Module,
8902            "",
8903            package_name.clone(),
8904            cpp_namespace_fq(&package_name),
8905        );
8906        let mut recovered = scope.clone();
8907        recovered.package_name = package_name;
8908        recovered.module = Some(module);
8909        recovered
8910    }
8911
8912    /// The unique namespace-scope forward declaration of `name` that precedes
8913    /// `recovered_node`, answered from the walk's carried-forward scan of the
8914    /// tree `recovered_node` belongs to.
8915    ///
8916    /// The scan is built on the first question and advanced by each later one,
8917    /// so a file that never reaches this path -- almost every file -- pays
8918    /// nothing, and one that reaches it thousands of times pays a single pass
8919    /// (#2754).
8920    fn unique_earlier_namespace_forward<'tree>(
8921        &mut self,
8922        recovered_node: Node<'tree>,
8923        name: &str,
8924        ancestry: &ParentIndex<'tree>,
8925    ) -> Option<String> {
8926        let mut root = recovered_node;
8927        while let Some(parent) = ancestry.parent(root) {
8928            root = parent;
8929        }
8930        let source = self.source;
8931        let scan = self
8932            .namespace_forward_scans
8933            .entry(CppTreeIdentity::of(root))
8934            .or_default();
8935        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
8936        let borrowed = scan.unique_earlier_forward(name, recovered_node);
8937
8938        #[cfg(debug_assertions)]
8939        assert_eq!(
8940            borrowed,
8941            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
8942            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
8943             for {name} at byte {}",
8944            recovered_node.start_byte()
8945        );
8946
8947        borrowed
8948    }
8949
8950    fn visit_malformed_function_definition_container<'tree>(
8951        &mut self,
8952        node: Node<'tree>,
8953        scope: &ScopeInfo,
8954        stack: &mut Vec<CppWork<'tree>>,
8955    ) {
8956        let Some(body) = cpp_body_node(node) else {
8957            return;
8958        };
8959        if !cpp_contains_namespace_definition(body) {
8960            return;
8961        }
8962        stack.push(CppWork::Container(CppContainer {
8963            node: body,
8964            scope: scope.clone(),
8965        }));
8966    }
8967
8968    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
8969    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
8970    /// emits for a sentinel-prefixed region, reparse the interior after the
8971    /// sentinel identifier as real C++ items -- confined to the region so
8972    /// every reparsed node keeps its original byte/line position -- and run the
8973    /// ordinary container visitation over the result. Returns `true` when it fired
8974    /// (the caller must then skip normal function processing). Nested sentinel
8975    /// regions recover recursively: the reparsed interior is walked through the
8976    /// same `visit_function_definition` path, so a sentinel inside the region hits
8977    /// this recovery again.
8978    /// Runs `reparse_walk` and records every declaration it mints as a
8979    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
8980    /// `recovery` (issue #1657). A reparsed sentinel region has no single
8981    /// recovered envelope unit: the ordinary visitors mint namespaces,
8982    /// classes, and members directly from the reparsed tree, so the walk's
8983    /// declaration delta is the recovered set. Records are ordered by
8984    /// declaration start byte so the parse product stays deterministic.
8985    fn record_recovered_declarations(
8986        &mut self,
8987        recovery: Range,
8988        origin: Node<'_>,
8989        reparse_walk: impl FnOnce(&mut Self),
8990    ) {
8991        // The recovered declarations begin at the recovery start, which can lie
8992        // past a branch directive the malformed origin swallowed (#6325:
8993        // `END_NS #else BEGIN_NS class C {};` parses as one declaration inside
8994        // the `#if` branch). Read their preprocessor context there.
8995        let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
8996            && recovery.start_byte < origin.end_byte())
8997        .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
8998        .flatten()
8999        .unwrap_or(origin);
9000        self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9001            let source_context = visitor.active_source_context.clone();
9002            visitor.record_recovered_declarations_with_context(
9003                recovery,
9004                source_context,
9005                reparse_walk,
9006            );
9007        });
9008    }
9009
9010    /// Run a recovered walk with an owned source context and record every
9011    /// declaration it mints. Deferred partitioned regions cannot retain their
9012    /// origin `Node`, so they capture the context while the origin tree is
9013    /// live and use this same materialization path when drained.
9014    fn record_recovered_declarations_with_context(
9015        &mut self,
9016        recovery: Range,
9017        source_context: Option<CppActiveSourceContext>,
9018        reparse_walk: impl FnOnce(&mut Self),
9019    ) {
9020        // The set difference this used to be, kept as the oracle every answer
9021        // is asserted against (#2787).
9022        #[cfg(any(debug_assertions, test))]
9023        let before = self.parsed.declarations().clone();
9024
9025        self.recovery_captures.push(CppRecoveryCapture::default());
9026        let previous = self.active_source_context.take();
9027        self.active_source_context = source_context;
9028        reparse_walk(self);
9029        self.active_source_context = previous;
9030        let captured = self
9031            .recovery_captures
9032            .pop()
9033            .expect("the capture this call pushed is the one it pops");
9034
9035        // The capture holds every declaration created while it was open, once
9036        // each and in creation order, so the recovered set costs what the
9037        // recovery made rather than everything the file has declared so far.
9038        // One filter is left to apply: a created declaration that a later
9039        // deferred replacement removed is not in the parse product to report.
9040        let mut minted: Vec<CodeUnit> = captured
9041            .created
9042            .into_iter()
9043            .filter(|unit| self.parsed.contains_declaration(unit))
9044            .collect();
9045        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9046
9047        #[cfg(any(debug_assertions, test))]
9048        {
9049            let mut rediscovered: Vec<CodeUnit> = self
9050                .parsed
9051                .declarations()
9052                .iter()
9053                .filter(|unit| !before.contains(*unit))
9054                .cloned()
9055                .collect();
9056            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9057            assert_eq!(
9058                minted, rediscovered,
9059                "the captured recovered set must be the declaration delta of the reparse \
9060                 walk over {recovery:?}"
9061            );
9062        }
9063
9064        for unit in minted {
9065            self.parsed
9066                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9067                    recovery,
9068                    unit,
9069                });
9070        }
9071    }
9072
9073    /// Where one recovered declaration sorts: by start byte, then by name, so
9074    /// the parse product stays deterministic.
9075    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9076        let start = self
9077            .parsed
9078            .declaration_ranges(unit)
9079            .first()
9080            .map(|range| range.start_byte)
9081            .unwrap_or(usize::MAX);
9082        (start, unit.fq_name().to_string())
9083    }
9084
9085    fn visit_sentinel_macro_region<'tree>(
9086        &mut self,
9087        node: Node<'tree>,
9088        scope: &ScopeInfo,
9089        stack: &mut Vec<CppWork<'tree>>,
9090        ancestry: &ParentIndex<'tree>,
9091    ) -> bool {
9092        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9093            return true;
9094        }
9095        if let Some((
9096            reparse_start,
9097            class_start,
9098            body_start,
9099            class_close_start,
9100            class_close_end,
9101            class_close_line,
9102        )) = cpp_sentinel_macro_class_region(node, self.source)
9103        {
9104            let Some(class_tree) =
9105                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9106            else {
9107                return false;
9108            };
9109            let class_root = class_tree.root_node();
9110            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9111            // A region reparse is its own tree and needs its own parent index.
9112            let class_ancestry = self.recovery_ancestry(class_root);
9113            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9114                class_root,
9115                template_node,
9116                self.source,
9117                &class_ancestry,
9118            ) else {
9119                return false;
9120            };
9121            let class_node = reparsed_class.declaration_node;
9122            let name = reparsed_class.name;
9123            let mut class_scope = scope.clone();
9124            if let Some(template_node) = template_node {
9125                class_scope.template_signature =
9126                    cpp_template_signature(template_node, class_node, self.source);
9127                class_scope.template_metadata =
9128                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9129            }
9130            let Some(body_tree) =
9131                cpp_reparse_region_items(self.source, body_start, class_close_start)
9132            else {
9133                return false;
9134            };
9135            let raw_supertypes = reparsed_class.raw_supertypes;
9136            let base_facts = reparsed_class.base_facts;
9137            let class_range = Range {
9138                start_byte: class_start,
9139                end_byte: class_close_end,
9140                start_line: class_node.start_position().row + 1,
9141                end_line: class_close_line,
9142            };
9143            let class_scope = self.scope_for_recovered_exported_class(
9144                class_node,
9145                &name,
9146                true,
9147                &class_scope,
9148                &class_ancestry,
9149            );
9150            let mut class_stack = Vec::new();
9151            self.with_recovery_source_context(node, |visitor| {
9152                let class_unit = visitor.visit_named_class_like_shape(
9153                    class_node,
9154                    name,
9155                    None,
9156                    true,
9157                    Some(class_range),
9158                    raw_supertypes,
9159                    base_facts,
9160                    &class_scope,
9161                    &mut class_stack,
9162                    &class_ancestry,
9163                );
9164                visitor.parsed.record_materialization(
9165                    MaterializationRecord::RecoveredDeclaration {
9166                        recovery: class_range,
9167                        unit: class_unit.clone(),
9168                    },
9169                );
9170                let member_scope = ScopeInfo {
9171                    package_name: class_scope.package_name.clone(),
9172                    module: class_scope.module.clone(),
9173                    class_unit: Some(class_unit),
9174                    template_signature: class_scope.template_signature.clone(),
9175                    template_metadata: None,
9176                    declarations_are_fields: true,
9177                    recovered_specialization_member_scope: false,
9178                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9179                };
9180                // The padded body reparse is its own tree, so it indexes itself.
9181                let body_root = body_tree.root_node();
9182                visitor.run_container_work(
9183                    body_root,
9184                    member_scope,
9185                    &visitor.recovery_ancestry(body_root),
9186                );
9187            });
9188            // Register only after the padded body reparse: its nodes deliberately
9189            // retain offsets inside the consumed region and must be visited first.
9190            self.consumed_fragment_regions
9191                .push((node.start_byte(), class_close_end));
9192            // An ERROR envelope can hold real sibling declarations after the
9193            // recovered class's close (the suffix-reparse boundary in
9194            // `cpp_sentinel_macro_class_region` partitions, it does not
9195            // consume). Walk the envelope's remaining children normally; the
9196            // consumed region above keeps the recovered class from being
9197            // indexed twice.
9198            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9199                stack.push(CppWork::Container(CppContainer {
9200                    node,
9201                    scope: scope.clone(),
9202                }));
9203            }
9204            return true;
9205        }
9206        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9207            return false;
9208        };
9209        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9210            return false;
9211        };
9212        let root = tree.root_node();
9213        if !cpp_reparsed_items_are_indexable(root, self.source) {
9214            return false;
9215        }
9216        let recovery = cpp_recovery_window(self.source, start, end);
9217        // The reparsed region is its own tree, so this walk indexes it itself.
9218        let reparsed_ancestry = self.recovery_ancestry(root);
9219        self.record_recovered_declarations(recovery, node, |visitor| {
9220            visitor.visit_container(
9221                root,
9222                &reparsed_ancestry,
9223                &scope.package_name,
9224                scope.module.clone(),
9225                scope.class_unit.clone(),
9226                scope.template_signature.clone(),
9227                scope.visible_using_namespaces.clone(),
9228            );
9229        });
9230        if end > node.end_byte() {
9231            self.consumed_fragment_regions
9232                .push((node.start_byte(), end));
9233        } else if node.kind() == "ERROR" && node.end_byte() > end {
9234            // The sentinel region ended at the first recovered class-like item
9235            // but the ERROR envelope keeps real sibling declarations after it
9236            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
9237            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
9238            // envelope's remaining children normally; the consumed region
9239            // keeps the reparsed prefix from being indexed twice.
9240            self.consumed_fragment_regions
9241                .push((node.start_byte(), end));
9242            stack.push(CppWork::Container(CppContainer {
9243                node,
9244                scope: scope.clone(),
9245            }));
9246        }
9247        true
9248    }
9249
9250    /// Re-own complete class declarations from the structured Abseil
9251    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
9252    /// its direct CST children already prove both namespace components and the
9253    /// class bodies, so the ordinary class/member visitor can retain ownership
9254    /// and exact source ranges without admitting unrelated callable bodies.
9255    fn visit_nested_namespace_sentinel<'tree>(
9256        &mut self,
9257        node: Node<'tree>,
9258        scope: &ScopeInfo,
9259        ancestry: &ParentIndex<'tree>,
9260    ) -> bool {
9261        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9262            return false;
9263        };
9264
9265        let mut package_name = scope.package_name.clone();
9266        let mut module = scope.module.clone();
9267        for component in recovered.namespace_components {
9268            package_name = if package_name.is_empty() {
9269                component
9270            } else {
9271                format!("{package_name}::{component}")
9272            };
9273            let namespace_module = CodeUnit::new_fq(
9274                self.file.clone(),
9275                CodeUnitType::Module,
9276                "",
9277                package_name.clone(),
9278                cpp_namespace_fq(&package_name),
9279            );
9280            if !self.parsed.contains_declaration(&namespace_module) {
9281                self.add_declaration(
9282                    ancestry,
9283                    namespace_module.clone(),
9284                    recovered.function,
9285                    None,
9286                    None,
9287                );
9288            }
9289            module = Some(namespace_module);
9290        }
9291
9292        let recovered_scope = ScopeInfo {
9293            package_name,
9294            module,
9295            // This scope's package comes from the namespace levels the sentinel
9296            // shape proves, so its owner must come from the same place. C++
9297            // never nests a namespace inside a class, so a surviving
9298            // `class_unit` here is recovery bleed from an earlier malformed
9299            // region, and keeping it would publish declarations whose package
9300            // names one lexical position and whose owner names another -- the
9301            // hybrid identity that trips the package/short boundary assert in
9302            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
9303            // identity-neutral for valid code, where `class_unit` is always
9304            // empty at a namespace.
9305            class_unit: None,
9306            template_signature: scope.template_signature.clone(),
9307            template_metadata: scope.template_metadata.clone(),
9308            declarations_are_fields: false,
9309            recovered_specialization_member_scope: false,
9310            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9311        };
9312        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9313            recovered.function,
9314            recovered.body,
9315            self.source,
9316            ancestry,
9317        ) {
9318            let mut class_scope = recovered_scope.clone();
9319            if let Some(template_node) = fragmented.template_node {
9320                class_scope.template_signature =
9321                    cpp_template_signature(template_node, fragmented.class_node, self.source);
9322                class_scope.template_metadata = cpp_template_metadata(
9323                    template_node,
9324                    fragmented.class_node,
9325                    self.source,
9326                    ancestry,
9327                );
9328            }
9329            if let Some(outcome) = self
9330                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9331            {
9332                let mut class_stack = Vec::new();
9333                let class_unit = self.visit_named_class_like_shape(
9334                    fragmented.class_node,
9335                    fragmented.name.clone(),
9336                    None,
9337                    true,
9338                    Some(fragmented.fragmented.class_range),
9339                    fragmented.raw_supertypes.clone(),
9340                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9341                    &class_scope,
9342                    &mut class_stack,
9343                    ancestry,
9344                );
9345                self.parsed
9346                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
9347                        recovery: fragmented.fragmented.class_range,
9348                        unit: class_unit.clone(),
9349                    });
9350                if self.visit_fragmented_export_class_members(
9351                    node,
9352                    outcome,
9353                    class_unit,
9354                    &class_scope,
9355                ) {
9356                    self.consumed_fragment_regions.push((
9357                        fragmented.consumed_start,
9358                        fragmented.fragmented.class_range.end_byte,
9359                    ));
9360                }
9361            }
9362        }
9363        // The class requirement above is the admission gate; once admitted,
9364        // traverse the whole proven inner namespace body so sibling aliases,
9365        // functions, and variables are not silently discarded. The body is a
9366        // node of the tree being walked, so it reuses that tree's index.
9367        self.run_container_work(recovered.body, recovered_scope, ancestry);
9368        true
9369    }
9370
9371    fn visit_declaration<'tree>(
9372        &mut self,
9373        node: Node<'tree>,
9374        scope: &ScopeInfo,
9375        in_class_body: bool,
9376        stack: &mut Vec<CppWork<'tree>>,
9377        ancestry: &ParentIndex<'tree>,
9378    ) {
9379        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9380            return;
9381        }
9382        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9383            return;
9384        }
9385        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9386            !cpp_active_template_type_parameter(
9387                node,
9388                node_text(declarator, self.source),
9389                self.source,
9390                ancestry,
9391            )
9392        }) {
9393            return;
9394        }
9395        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9396            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
9397            // the following `Imaging image` member into one malformed field.
9398            // The ERROR's identifier is the actual declarator; route it
9399            // through the ordinary field path so its parent, range, and
9400            // signature metadata remain consistent with every other member.
9401            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9402            return;
9403        }
9404        if in_class_body
9405            && let Some(parent) = scope.class_unit.as_ref()
9406            && let Some(call) =
9407                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9408        {
9409            self.visit_recovered_macro_qualified_constructor_definition(
9410                node, call, scope, ancestry,
9411            );
9412            return;
9413        }
9414        if in_class_body
9415            && let Some(parent) = scope.class_unit.as_ref()
9416            && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9417                node,
9418                parent.identifier(),
9419                self.source,
9420            )
9421        {
9422            self.visit_recovered_macro_initialized_constructor_definition(
9423                node,
9424                constructor,
9425                scope,
9426                ancestry,
9427            );
9428            return;
9429        }
9430        if in_class_body
9431            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9432        {
9433            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9434            return;
9435        }
9436        if in_class_body
9437            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9438        {
9439            for member in members {
9440                self.add_macro_wrapped_declaration(member, scope, ancestry);
9441            }
9442            return;
9443        }
9444        if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9445            return;
9446        }
9447        if in_class_body
9448            && let Some(declarators) =
9449                recovered_macro_qualified_field_declarators(node, self.source)
9450        {
9451            for declarator in declarators {
9452                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9453            }
9454            return;
9455        }
9456        let recovered_alias_names = recovered_type_alias_names(node, self.source);
9457        if !recovered_alias_names.is_empty() {
9458            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9459            return;
9460        }
9461        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9462            return;
9463        }
9464        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9465            return;
9466        }
9467
9468        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9469            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9470                // Issue #938: the members tree-sitter scattered out of the fragmented
9471                // multiple-base export node are reparsed from their true body region
9472                // and re-owned as members of the recovered class, with an explicit
9473                // navigation range spanning to the displaced closing brace.
9474                if let Some(outcome) =
9475                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9476                {
9477                    let consumed_region = (
9478                        recovered.declaration_node.end_byte(),
9479                        fragmented.class_range.end_byte,
9480                    );
9481                    let code_unit = self.visit_named_class_like_shape(
9482                        recovered.declaration_node,
9483                        recovered.name,
9484                        None,
9485                        true,
9486                        Some(fragmented.class_range),
9487                        recovered.raw_supertypes,
9488                        recovered.base_facts,
9489                        scope,
9490                        stack,
9491                        ancestry,
9492                    );
9493                    self.parsed.record_materialization(
9494                        MaterializationRecord::RecoveredDeclaration {
9495                            recovery: fragmented.class_range,
9496                            unit: code_unit.clone(),
9497                        },
9498                    );
9499                    let consume_fragment =
9500                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9501                    // Everything between the fragmented declaration and its displaced
9502                    // closing brace now belongs to the recovered class; keep the
9503                    // ordinary walk from re-indexing those scattered siblings at top
9504                    // level. Register the consumed region only after indexing because
9505                    // the reparsed nodes retain byte offsets inside that same region.
9506                    if consume_fragment {
9507                        self.consumed_fragment_regions.push(consumed_region);
9508                    }
9509                    return;
9510                }
9511            }
9512            let uses_initializer_body = recovered.uses_initializer_body;
9513            let definition_body_present = recovered.body.is_some();
9514            let class_unit = self.visit_named_class_like_shape(
9515                recovered.declaration_node,
9516                recovered.name,
9517                recovered.body,
9518                definition_body_present,
9519                None,
9520                recovered.raw_supertypes,
9521                recovered.base_facts,
9522                scope,
9523                stack,
9524                ancestry,
9525            );
9526            self.parsed
9527                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9528                    recovery: cpp_declaration_range(node),
9529                    unit: class_unit,
9530                });
9531            if uses_initializer_body {
9532                return;
9533            }
9534        }
9535
9536        // A function-like macro invocation that expands to declarations keeps
9537        // no terminator of its own: the semicolons live in the replacement
9538        // body, which is not part of this tree. Tree-sitter recovers one and
9539        // reads the macro name as the member type and its argument list as a
9540        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
9541        // published a field named `ClosedDetect` beside the real constructor
9542        // and split every reference to the constructor's name between the two
9543        // (#3298). The invented terminator is the whole difference from a real
9544        // member: a member terminates itself, and with a terminator the
9545        // grammar reads `T (name);` as a declaration rather than a field.
9546        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9547            return;
9548        }
9549
9550        let mut handled_function = false;
9551        let mut handled_declarator = false;
9552        let mut cursor = node.walk();
9553        for child in node.named_children(&mut cursor) {
9554            if matches!(
9555                child.kind(),
9556                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9557            ) {
9558                // A named class-like definition remains a declaration even when
9559                // the same statement also declares an object, for example
9560                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
9561                // declaration's type and `kind` as its declarator.  Dropping the
9562                // type here loses both its nested owner and every later lexical
9563                // reference to it.  A body is the structured proof that this is
9564                // a definition rather than an elaborated type use such as
9565                // `class Kind value;`.
9566                if cpp_body_node(child).is_some() {
9567                    self.visit_class_like(child, scope, stack, ancestry);
9568                }
9569                continue;
9570            }
9571        }
9572
9573        let mut cursor = node.walk();
9574        for child in node.children_by_field_name("declarator", &mut cursor) {
9575            if crate::structural::is_recovered_designator_init_declarator(child) {
9576                handled_declarator = true;
9577                continue;
9578            }
9579            if in_class_body
9580                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9581            {
9582                handled_declarator = true;
9583                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9584                continue;
9585            }
9586            if let Some(kind) = classify_declarator(child) {
9587                handled_declarator = true;
9588                match kind {
9589                    DeclaratorKind::Function(function_declarator) => {
9590                        handled_function = true;
9591                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9592                    }
9593                    DeclaratorKind::Variable(variable_declarator) => {
9594                        self.visit_variable_declaration(
9595                            node,
9596                            variable_declarator,
9597                            scope,
9598                            in_class_body,
9599                            ancestry,
9600                        );
9601                    }
9602                }
9603            }
9604        }
9605
9606        if !handled_declarator {
9607            let mut cursor = node.walk();
9608            for child in node.named_children(&mut cursor) {
9609                if crate::structural::is_recovered_designator_init_declarator(child) {
9610                    handled_declarator = true;
9611                    continue;
9612                }
9613                if !is_unfielded_declarator_candidate(child) {
9614                    continue;
9615                }
9616                let Some(kind) = classify_declarator(child) else {
9617                    continue;
9618                };
9619                handled_declarator = true;
9620                match kind {
9621                    DeclaratorKind::Function(function_declarator) => {
9622                        handled_function = true;
9623                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9624                    }
9625                    DeclaratorKind::Variable(variable_declarator) => {
9626                        self.visit_variable_declaration(
9627                            node,
9628                            variable_declarator,
9629                            scope,
9630                            in_class_body,
9631                            ancestry,
9632                        );
9633                    }
9634                }
9635            }
9636        }
9637
9638        if handled_function {
9639            return;
9640        }
9641
9642        if !handled_declarator {
9643            if in_class_body {
9644                self.visit_class_members_from_declaration(node, scope, ancestry);
9645            } else {
9646                self.visit_global_variables_from_declaration(node, scope, ancestry);
9647            }
9648        }
9649    }
9650
9651    /// Preserve the member structure of an anonymous aggregate.
9652    ///
9653    /// An anonymous union with no declarator promotes its fields into the
9654    /// containing aggregate. An anonymous struct/union followed by a named
9655    /// declarator, such as `struct { T *ops; } sock`, declares both the field
9656    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
9657    /// the declarator's structured nested identity so a later `value.sock.ops`
9658    /// chain can traverse it without parsing a type spelling (#2407).
9659    ///
9660    /// An anonymous aggregate declares no tag, so there is nothing for the C
9661    /// dialect to re-scope and nothing for the C++ dialect to nest: both
9662    /// readings of this shape are the same, and both need these members
9663    /// (#3301).
9664    fn anonymous_aggregate_declaration_parts<'tree>(
9665        node: Node<'tree>,
9666    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9667        let aggregate = node.child_by_field_name("type")?;
9668        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9669            || aggregate.child_by_field_name("name").is_some()
9670        {
9671            return None;
9672        }
9673        let body = cpp_body_node(aggregate)?;
9674        let mut cursor = node.walk();
9675        let declarators = node
9676            .children_by_field_name("declarator", &mut cursor)
9677            .filter_map(|declarator| match classify_declarator(declarator) {
9678                Some(DeclaratorKind::Variable(variable)) => Some(variable),
9679                Some(DeclaratorKind::Function(_)) | None => None,
9680            })
9681            .collect();
9682        Some((aggregate, body, declarators))
9683    }
9684
9685    fn visit_anonymous_aggregate_declaration<'tree>(
9686        &mut self,
9687        node: Node<'tree>,
9688        scope: &ScopeInfo,
9689        in_class_body: bool,
9690        stack: &mut Vec<CppWork<'tree>>,
9691        ancestry: &ParentIndex<'tree>,
9692    ) -> bool {
9693        if !in_class_body || scope.class_unit.is_none() {
9694            return false;
9695        }
9696        let Some((aggregate, body, declarators)) =
9697            Self::anonymous_aggregate_declaration_parts(node)
9698        else {
9699            return false;
9700        };
9701        if declarators.is_empty() {
9702            stack.push(CppWork::Container(CppContainer {
9703                node: body,
9704                scope: scope.clone(),
9705            }));
9706            return true;
9707        }
9708
9709        for declarator in declarators {
9710            let Some(name) = extract_variable_name(declarator, self.source) else {
9711                continue;
9712            };
9713            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9714            // The receiver type's identity is the declarator's name, so its
9715            // range is the field declaration that carries that name token.
9716            // The inner specifier ends at the closing brace, which leaves the
9717            // name the unit is known by outside its own text (#3287).
9718            self.visit_named_class_like_shape(
9719                aggregate,
9720                name,
9721                Some(body),
9722                true,
9723                Some(cpp_declaration_range(node)),
9724                None,
9725                None,
9726                scope,
9727                stack,
9728                ancestry,
9729            );
9730        }
9731        true
9732    }
9733
9734    /// Give a function-local anonymous aggregate a structured owner so its
9735    /// direct pointer bindings can be typed by the usage graph.  Unlike an
9736    /// anonymous aggregate in a class body, there is no source-level tag or
9737    /// typedef to supply an identity.  The aggregate's CST range is therefore
9738    /// part of a generated, collision-resistant identity; no source spelling
9739    /// is consulted.  The declaration's own variable remains a file-level
9740    /// field projection, while the aggregate body is visited as the generated
9741    /// class's field list.
9742    fn visit_anonymous_local_aggregate_declaration<'tree>(
9743        &mut self,
9744        node: Node<'tree>,
9745        scope: &ScopeInfo,
9746        stack: &mut Vec<CppWork<'tree>>,
9747        ancestry: &ParentIndex<'tree>,
9748    ) -> bool {
9749        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
9750            return false;
9751        }
9752        let Some((aggregate, body, declarators)) =
9753            Self::anonymous_aggregate_declaration_parts(node)
9754        else {
9755            return false;
9756        };
9757        if declarators.is_empty() {
9758            return false;
9759        }
9760
9761        for declarator in &declarators {
9762            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
9763        }
9764        let name = format!("<anonymous:{}>", aggregate.start_byte());
9765        self.visit_named_class_like_shape(
9766            aggregate,
9767            name,
9768            Some(body),
9769            true,
9770            None,
9771            None,
9772            None,
9773            scope,
9774            stack,
9775            ancestry,
9776        );
9777        true
9778    }
9779
9780    fn visit_function_declaration<'tree>(
9781        &mut self,
9782        declaration_node: Node<'tree>,
9783        declarator: Node<'tree>,
9784        scope: &ScopeInfo,
9785        ancestry: &ParentIndex<'tree>,
9786    ) {
9787        let Some(function) = extract_function_info(declarator, self.source, scope) else {
9788            return;
9789        };
9790        // An in-class member prototype is a real source declaration, not a
9791        // generated or recovery-only unit, so it keeps the ordinary code-unit
9792        // identity (#3508). #3493 recorded its callable modifiers, which is
9793        // what a canonical procedure key needs; a synthetic unit would still
9794        // refuse that key on identity grounds alone.
9795        let code_unit = function.code_unit(self.file.clone());
9796        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
9797            return;
9798        }
9799        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9800        let signature = render_cpp_function_display_signature_from_node(
9801            declaration_node,
9802            self.source,
9803            scope.template_signature.as_deref(),
9804            false,
9805            ancestry,
9806        );
9807        self.add_signature_with_metadata(
9808            code_unit.clone(),
9809            cpp_signature_metadata(
9810                signature,
9811                declarator,
9812                cpp_callable_linkage(declaration_node, self.source, ancestry),
9813                self.source,
9814                ancestry,
9815            )
9816            .with_declaration_only(true),
9817        );
9818        if let Some(parent) = &scope.class_unit {
9819            self.parsed.add_child(parent.clone(), code_unit);
9820        } else if let Some(module) = &scope.module {
9821            self.parsed.add_child(module.clone(), code_unit);
9822        }
9823    }
9824
9825    fn link_existing_source_declaration(
9826        &mut self,
9827        ancestry: &ParentIndex<'_>,
9828        code_unit: &CodeUnit,
9829        declaration_node: Node<'_>,
9830    ) -> bool {
9831        if !self.parsed.contains_declaration(code_unit) {
9832            return false;
9833        }
9834        self.link_source_declaration(ancestry, code_unit, declaration_node);
9835        self.parsed
9836            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
9837        true
9838    }
9839
9840    fn visit_recovered_macro_qualified_function_declaration<'tree>(
9841        &mut self,
9842        declaration_node: Node<'tree>,
9843        call: Node<'tree>,
9844        scope: &ScopeInfo,
9845        ancestry: &ParentIndex<'tree>,
9846    ) {
9847        let Some(parent) = &scope.class_unit else {
9848            return;
9849        };
9850        let Some(name_node) = call.child_by_field_name("function") else {
9851            return;
9852        };
9853        let Some(arguments) = call.child_by_field_name("arguments") else {
9854            return;
9855        };
9856        let Some((signature, parameter_labels)) =
9857            recovered_macro_qualified_function_parameters(arguments, self.source)
9858        else {
9859            return;
9860        };
9861        let arity = parameter_labels.len();
9862        let function = FunctionInfo {
9863            package_name: scope.package_name.clone(),
9864            owner: Some(CppMemberOwner::Unit(parent.clone())),
9865            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
9866            signature,
9867        };
9868        if function.name.is_empty() {
9869            return;
9870        }
9871        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
9872        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
9873            return;
9874        }
9875        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9876        let signature_label = render_cpp_function_display_signature_from_node(
9877            declaration_node,
9878            self.source,
9879            scope.template_signature.as_deref(),
9880            false,
9881            ancestry,
9882        );
9883        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
9884        // Only the callable's parameters came from the reparse: the
9885        // `field_declaration` this recovery reads is a real node in a real
9886        // class body, so its storage class and the body's access ladder state
9887        // this member's modifiers exactly as an ordinary member's do.
9888        let modifiers =
9889            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
9890        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
9891            .with_declaration_only(true)
9892            .with_callable_arity(CallableArity::exact(arity))
9893            .with_callable_linkage(linkage)
9894            .with_callable_modifiers(
9895                modifiers.is_static,
9896                modifiers.is_constructor,
9897                modifiers.visibility,
9898            );
9899        self.add_signature_with_metadata(code_unit.clone(), metadata);
9900        self.parsed.add_child(parent.clone(), code_unit);
9901    }
9902
9903    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
9904        &mut self,
9905        declaration_node: Node<'tree>,
9906        call: Node<'tree>,
9907        scope: &ScopeInfo,
9908        ancestry: &ParentIndex<'tree>,
9909    ) {
9910        let Some(parent) = &scope.class_unit else {
9911            return;
9912        };
9913        let Some(arguments) = call.child_by_field_name("arguments") else {
9914            return;
9915        };
9916        let Some((mut signature, parameter_labels)) =
9917            recovered_macro_qualified_function_parameters(arguments, self.source)
9918        else {
9919            return;
9920        };
9921        if let Some(template_signature) = &scope.template_signature {
9922            signature = format!("{template_signature}{signature}");
9923        }
9924        let arity = parameter_labels.len();
9925        let function = FunctionInfo {
9926            package_name: scope.package_name.clone(),
9927            owner: Some(CppMemberOwner::Unit(parent.clone())),
9928            name: parent.identifier().to_string(),
9929            signature,
9930        };
9931        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
9932        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9933        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
9934        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
9935        // The recovery already proved this member is the class's constructor:
9936        // the reparsed call names the enclosing class. Everything else is read
9937        // from the real `field_declaration` the recovery found it in.
9938        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
9939        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
9940            .with_declaration_only(false)
9941            .with_callable_arity(CallableArity::exact(arity))
9942            .with_callable_linkage(linkage)
9943            .with_callable_modifiers(
9944                modifiers.is_static,
9945                modifiers.is_constructor,
9946                modifiers.visibility,
9947            );
9948        self.add_signature_with_metadata(code_unit.clone(), metadata);
9949        self.parsed.add_child(parent.clone(), code_unit);
9950    }
9951
9952    /// Publish the constructor of the macro-decorated field shape that
9953    /// `recovered_macro_initialized_constructor_declarator` proves. The head
9954    /// is a real, clean function declarator, so it keeps the ordinary
9955    /// identity and metadata; the `default_value` initializer list is the
9956    /// constructor body, so the occurrence is a definition.
9957    fn visit_recovered_macro_initialized_constructor_definition<'tree>(
9958        &mut self,
9959        declaration_node: Node<'tree>,
9960        function_declarator: Node<'tree>,
9961        scope: &ScopeInfo,
9962        ancestry: &ParentIndex<'tree>,
9963    ) {
9964        let Some(parent) = &scope.class_unit else {
9965            return;
9966        };
9967        let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
9968            return;
9969        };
9970        let code_unit = function.code_unit(self.file.clone());
9971        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
9972        // Render the head the way an ordinary constructor definition renders
9973        // the text ahead of its body, member initializers included.
9974        let body_start = declaration_node
9975            .child_by_field_name("default_value")
9976            .map_or(declaration_node.end_byte(), |body| body.start_byte());
9977        let head =
9978            collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
9979        let signature = match scope.template_signature.as_deref() {
9980            Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
9981            None => format!("{head} {{...}}"),
9982        };
9983        self.add_signature_with_metadata(
9984            code_unit.clone(),
9985            cpp_signature_metadata(
9986                signature,
9987                function_declarator,
9988                cpp_callable_linkage(declaration_node, self.source, ancestry),
9989                self.source,
9990                ancestry,
9991            )
9992            .with_declaration_only(false),
9993        );
9994        self.parsed.add_child(parent.clone(), code_unit);
9995    }
9996
9997    fn visit_variable_declaration<'tree>(
9998        &mut self,
9999        declaration_node: Node<'tree>,
10000        declarator: Node<'tree>,
10001        scope: &ScopeInfo,
10002        in_class_body: bool,
10003        ancestry: &ParentIndex<'tree>,
10004    ) {
10005        let Some(name) = extract_variable_name(declarator, self.source) else {
10006            return;
10007        };
10008        let parent = if in_class_body {
10009            let Some(parent) = &scope.class_unit else {
10010                return;
10011            };
10012            Some(parent)
10013        } else {
10014            None
10015        };
10016        let short_name = match parent {
10017            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10018            None => name.clone(),
10019        };
10020        let fq = cpp_leaf_fq(
10021            &scope.package_name,
10022            parent,
10023            &name,
10024            SegmentKind::Member,
10025            SegmentKind::Member,
10026        );
10027        let code_unit = CodeUnit::new_fq(
10028            self.file.clone(),
10029            CodeUnitType::Field,
10030            scope.package_name.clone(),
10031            short_name,
10032            fq,
10033        );
10034        if self.parsed.contains_declaration(&code_unit) {
10035            return;
10036        }
10037        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10038        self.add_signature_with_metadata(
10039            code_unit.clone(),
10040            SignatureMetadata::new(
10041                render_cpp_field_signature(declaration_node, declarator, self.source),
10042                Vec::new(),
10043            )
10044            .with_cpp_field_linkage(cpp_field_declaration_linkage(
10045                declaration_node,
10046                self.source,
10047                ancestry,
10048            )),
10049        );
10050        if let Some(parent) = &scope.class_unit {
10051            self.parsed.add_child(parent.clone(), code_unit);
10052        } else if let Some(module) = &scope.module {
10053            self.parsed.add_child(module.clone(), code_unit);
10054        }
10055    }
10056
10057    /// Index the field half of an omitted-semicolon pair the macro-declaration
10058    /// grammar folded into the following function definition's head. The
10059    /// function itself continues through the ordinary visitor path.
10060    fn visit_omitted_semicolon_macro_field<'tree>(
10061        &mut self,
10062        node: Node<'tree>,
10063        type_modifier: Node<'tree>,
10064        name_modifier: Node<'tree>,
10065        scope: &ScopeInfo,
10066        ancestry: &ParentIndex<'tree>,
10067    ) {
10068        let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10069            return;
10070        };
10071        let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10072        if name.is_empty() {
10073            return;
10074        }
10075        let range = Range {
10076            start_byte: type_modifier.start_byte(),
10077            end_byte: name_modifier.end_byte(),
10078            start_line: type_modifier.start_position().row + 1,
10079            end_line: name_modifier.end_position().row + 1,
10080        };
10081        self.record_macro_head_field(node, range, name, scope, ancestry);
10082    }
10083
10084    /// Index the macro-return stand-in field: a clean function definition whose
10085    /// macro-modified head returns an active template type parameter. The field
10086    /// is named after the return type and spans the head up to it, exactly the
10087    /// phantom `field_declaration` the older grammar emitted (#1129).
10088    fn visit_macro_return_template_parameter_field<'tree>(
10089        &mut self,
10090        node: Node<'tree>,
10091        head_start: Node<'tree>,
10092        type_node: Node<'tree>,
10093        scope: &ScopeInfo,
10094        ancestry: &ParentIndex<'tree>,
10095    ) {
10096        let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10097        let range = Range {
10098            start_byte: head_start.start_byte(),
10099            end_byte: type_node.end_byte(),
10100            start_line: head_start.start_position().row + 1,
10101            end_line: type_node.end_position().row + 1,
10102        };
10103        self.record_macro_head_field(node, range, name, scope, ancestry);
10104    }
10105
10106    /// Record one field recovered from a function definition's macro head. The
10107    /// signature is the head's own text: like the older grammar's phantom
10108    /// field_declaration, it carries no semicolon because none was written.
10109    fn record_macro_head_field<'tree>(
10110        &mut self,
10111        node: Node<'tree>,
10112        range: Range,
10113        name: String,
10114        scope: &ScopeInfo,
10115        ancestry: &ParentIndex<'tree>,
10116    ) {
10117        if name.is_empty() {
10118            return;
10119        }
10120        let parent = scope.class_unit.as_ref();
10121        let short_name = match parent {
10122            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10123            None => name.clone(),
10124        };
10125        let code_unit = CodeUnit::new_fq(
10126            self.file.clone(),
10127            CodeUnitType::Field,
10128            scope.package_name.clone(),
10129            short_name,
10130            cpp_leaf_fq(
10131                &scope.package_name,
10132                parent,
10133                &name,
10134                SegmentKind::Member,
10135                SegmentKind::Member,
10136            ),
10137        );
10138        if self.parsed.contains_declaration(&code_unit) {
10139            return;
10140        }
10141        self.add_declaration_with_range(
10142            ancestry,
10143            code_unit.clone(),
10144            range,
10145            Some(node),
10146            parent.cloned(),
10147            None,
10148        );
10149        let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10150        self.add_signature_with_metadata(
10151            code_unit.clone(),
10152            SignatureMetadata::new(signature, Vec::new())
10153                .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10154        );
10155        if let Some(parent) = &scope.class_unit {
10156            self.parsed.add_child(parent.clone(), code_unit);
10157        } else if let Some(module) = &scope.module {
10158            self.parsed.add_child(module.clone(), code_unit);
10159        }
10160    }
10161
10162    fn variable_declarator_from_declaration_child<'tree>(
10163        child: Node<'tree>,
10164    ) -> Option<Node<'tree>> {
10165        if child.kind() == "init_declarator" {
10166            child.child_by_field_name("declarator")
10167        } else if matches!(
10168            child.kind(),
10169            "identifier"
10170                | "field_identifier"
10171                | "pointer_declarator"
10172                | "reference_declarator"
10173                | "array_declarator"
10174                | "parenthesized_declarator"
10175                | "macro_modifier_declarator"
10176        ) {
10177            Some(child)
10178        } else {
10179            None
10180        }
10181    }
10182
10183    fn visit_class_members_from_declaration<'tree>(
10184        &mut self,
10185        node: Node<'tree>,
10186        scope: &ScopeInfo,
10187        ancestry: &ParentIndex<'tree>,
10188    ) {
10189        let mut cursor = node.walk();
10190        for child in node.named_children(&mut cursor) {
10191            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10192                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10193            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10194            {
10195                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10196            }
10197        }
10198    }
10199
10200    fn visit_global_variables_from_declaration<'tree>(
10201        &mut self,
10202        node: Node<'tree>,
10203        scope: &ScopeInfo,
10204        ancestry: &ParentIndex<'tree>,
10205    ) {
10206        let mut cursor = node.walk();
10207        for child in node.named_children(&mut cursor) {
10208            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10209                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10210            }
10211        }
10212    }
10213
10214    fn visit_type_declaration<'tree>(
10215        &mut self,
10216        node: Node<'tree>,
10217        scope: &ScopeInfo,
10218        stack: &mut Vec<CppWork<'tree>>,
10219        ancestry: &ParentIndex<'tree>,
10220    ) {
10221        let type_node = node.child_by_field_name("type");
10222        if let Some(type_node) = type_node
10223            && matches!(
10224                type_node.kind(),
10225                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10226            )
10227        {
10228            self.visit_class_like(type_node, scope, stack, ancestry);
10229        }
10230
10231        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10232            let range = Range {
10233                start_byte: node.start_byte(),
10234                end_byte: recovered.end_node.end_byte(),
10235                start_line: node.start_position().row + 1,
10236                end_line: recovered.end_node.end_position().row + 1,
10237            };
10238            let signature = self
10239                .source
10240                .get(range.start_byte..range.end_byte)
10241                .map(normalize_cpp_whitespace)
10242                .unwrap_or_default();
10243            self.record_type_aliases(
10244                ancestry,
10245                node,
10246                scope,
10247                vec![recovered.name],
10248                signature,
10249                range,
10250            );
10251            return;
10252        }
10253
10254        let alias_names = match node.kind() {
10255            "alias_declaration" => extract_alias_declaration_name(node, self.source)
10256                .into_iter()
10257                .collect::<Vec<_>>(),
10258            "type_definition" => extract_typedef_alias_names(node, self.source),
10259            _ => Vec::new(),
10260        };
10261        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10262            (type_node, alias_names.as_slice())
10263            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10264            && type_node.child_by_field_name("name").is_none()
10265        {
10266            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10267        } else {
10268            None
10269        };
10270        self.add_type_aliases(ancestry, node, scope, alias_names);
10271        if let Some((body, alias_name)) = anonymous_aggregate {
10272            // The typedef alias is also the only user-visible identity of an
10273            // anonymous aggregate. Reuse it as the member owner instead of
10274            // minting a second signatureless class with the same FQN. The
10275            // latter makes forward lookup ambiguous when conditional aliases
10276            // coexist and returns duplicate definitions even without guards.
10277            let signature = normalize_cpp_whitespace(node_text(node, self.source));
10278            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10279            debug_assert!(self.parsed.contains_declaration(&alias_unit));
10280            let mut nested_scope = scope.clone();
10281            nested_scope.class_unit = Some(alias_unit);
10282            nested_scope.template_signature = scope.template_signature.clone();
10283            nested_scope.template_metadata = None;
10284            nested_scope.declarations_are_fields = false;
10285            nested_scope.recovered_specialization_member_scope = false;
10286            stack.push(CppWork::Container(CppContainer {
10287                node: body,
10288                scope: nested_scope,
10289            }));
10290        }
10291    }
10292
10293    fn add_type_aliases(
10294        &mut self,
10295        ancestry: &ParentIndex<'_>,
10296        node: Node<'_>,
10297        scope: &ScopeInfo,
10298        alias_names: Vec<String>,
10299    ) {
10300        let signature = normalize_cpp_whitespace(node_text(node, self.source));
10301        self.record_type_aliases(
10302            ancestry,
10303            node,
10304            scope,
10305            alias_names,
10306            signature,
10307            cpp_declaration_range(node),
10308        );
10309    }
10310
10311    fn record_type_aliases(
10312        &mut self,
10313        ancestry: &ParentIndex<'_>,
10314        node: Node<'_>,
10315        scope: &ScopeInfo,
10316        alias_names: Vec<String>,
10317        signature: String,
10318        range: Range,
10319    ) {
10320        if signature.is_empty() {
10321            return;
10322        }
10323        let type_name = node
10324            .child_by_field_name("type")
10325            .and_then(|type_node| type_node.child_by_field_name("name"))
10326            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10327        for alias_name in alias_names {
10328            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10329                continue;
10330            }
10331            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10332            // Declaration identity does not include the alias signature. Keep
10333            // each physical range so conditional aliases retain their guards.
10334            self.note_declaration(&code_unit);
10335            self.link_source_declaration(ancestry, &code_unit, node);
10336            self.parsed
10337                .add_code_unit_with_range(code_unit.clone(), range, None, None);
10338            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
10339            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10340                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
10341            });
10342            self.add_signature_with_metadata(
10343                code_unit.clone(),
10344                SignatureMetadata::new(signature.clone(), Vec::new())
10345                    .with_underlying_type_identity(underlying_type_identity),
10346            );
10347            if let Some(metadata) = &scope.template_metadata {
10348                let mut metadata = metadata.clone();
10349                metadata.primary_fq_name = code_unit.fq_name();
10350                self.parsed
10351                    .set_cpp_template_metadata(code_unit.clone(), metadata);
10352            }
10353            if let Some(parent) = &scope.class_unit {
10354                self.parsed.add_child(parent.clone(), code_unit.clone());
10355            } else if let Some(module) = &scope.module {
10356                self.parsed.add_child(module.clone(), code_unit.clone());
10357            }
10358            self.parsed.mark_type_alias(code_unit);
10359        }
10360    }
10361
10362    fn type_alias_unit(
10363        &self,
10364        scope: &ScopeInfo,
10365        alias_name: String,
10366        signature: String,
10367    ) -> CodeUnit {
10368        let short_name = if let Some(parent) = &scope.class_unit {
10369            cpp_join_nested_short(parent.short_name(), &alias_name)
10370        } else {
10371            alias_name.clone()
10372        };
10373        let fq = cpp_leaf_fq(
10374            &scope.package_name,
10375            scope.class_unit.as_ref(),
10376            &alias_name,
10377            SegmentKind::Nested,
10378            SegmentKind::Type,
10379        );
10380        CodeUnit::with_signature_and_fq(
10381            self.file.clone(),
10382            CodeUnitType::Class,
10383            scope.package_name.clone(),
10384            short_name,
10385            Some(signature),
10386            false,
10387            fq,
10388        )
10389    }
10390
10391    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10392        // A comment can terminate tree-sitter's preproc_arg before the logical
10393        // directive ends. Its remaining declarations are replacement locals,
10394        // not file-scope fields, even when recovery exposes them as siblings.
10395        if let Some(replacement) =
10396            crate::graph::syntax::function_macro_replacement_span(node, self.source)
10397        {
10398            self.consumed_fragment_regions
10399                .push((replacement.start, replacement.end));
10400        }
10401        let Some(name) = extract_macro_name(node, self.source) else {
10402            return;
10403        };
10404        let signature = node_text(node, self.source).trim_end().to_string();
10405        if signature.is_empty() {
10406            return;
10407        }
10408        let fq = cpp_member_fq("", &name);
10409        // A macro can be undefined and redefined later in the same file. Its
10410        // structured directive is part of the declaration identity so the
10411        // temporal environment can navigate to the definition active at a
10412        // reference instead of collapsing every spelling to the first range.
10413        // The same physical directive parsed through another C/C++ reading
10414        // still produces the same unit and remains deduplicated.
10415        let code_unit = CodeUnit::with_signature_and_fq(
10416            self.file.clone(),
10417            CodeUnitType::Macro,
10418            "",
10419            name.clone(),
10420            Some(signature.clone()),
10421            false,
10422            fq,
10423        );
10424        if !self.parsed.contains_declaration(&code_unit) {
10425            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10426            let name_range = node
10427                .child_by_field_name("name")
10428                .map(cpp_declaration_range)
10429                .unwrap_or_else(|| cpp_declaration_range(node));
10430            self.parsed
10431                .record_materialization(MaterializationRecord::GeneratedDeclaration {
10432                    site: cpp_declaration_range(node),
10433                    argument: name_range,
10434                    kind: GenerationKind::PreprocessorDefinition,
10435                    unit: code_unit.clone(),
10436                });
10437            self.parsed.add_signature(code_unit, signature);
10438        }
10439        if node.kind() == "preproc_def" {
10440            update_object_macro_field_environment(
10441                node,
10442                self.source,
10443                &mut self.object_macro_fields,
10444                &mut self.ambiguous_object_macro_fields,
10445            );
10446        } else {
10447            self.object_macro_fields.remove(&name);
10448            self.ambiguous_object_macro_fields.remove(&name);
10449        }
10450    }
10451
10452    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10453        let Some(directive) = node.child_by_field_name("directive") else {
10454            return;
10455        };
10456        let name = node_text(directive, self.source).trim();
10457        let range = cpp_declaration_range(node);
10458        let fields = object_macro_field_closure(&self.object_macro_fields, name);
10459        self.materialize_object_macro_fields(fields, range, node, scope);
10460    }
10461
10462    /// Bare object-like field-list macros inside an otherwise well-formed
10463    /// aggregate remain identifier nodes in a field declaration. More than
10464    /// one adjacent invocation can be folded into the same declaration, so
10465    /// inspect all of its structured identifier nodes.
10466    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10467        if !matches!(node.kind(), "declaration" | "field_declaration") {
10468            return false;
10469        }
10470        // Claiming the declaration means its members are accounted for, which
10471        // is only true when there is an owner to materialize them into. A file
10472        // scope reaches this with a declaration that merely *contains* an
10473        // aggregate spelling an invocation (tree-sitter wraps libuv's
10474        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
10475        // `field_declaration` under a malformed header), and consuming it there
10476        // would discard the aggregate itself (#2985).
10477        if scope.class_unit.is_none() {
10478            return false;
10479        }
10480        let macro_nodes =
10481            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10482        for macro_node in &macro_nodes {
10483            let name = node_text(*macro_node, self.source).trim();
10484            let fields = object_macro_field_closure(&self.object_macro_fields, name);
10485            self.materialize_object_macro_fields(
10486                fields,
10487                cpp_declaration_range(*macro_node),
10488                *macro_node,
10489                scope,
10490            );
10491        }
10492        let Some(last) = macro_nodes.last() else {
10493            return false;
10494        };
10495        // Claiming the declaration accounts for every member it spells, not
10496        // only the donated ones: an invocation folded together with the
10497        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
10498        // leaves those members shapeless, and the grammar recovers them from
10499        // the byte range that follows the invocation.
10500        self.record_collapsed_aggregate_fields(
10501            last.end_byte()..node.end_byte(),
10502            node.start_position().row
10503                + 1
10504                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10505            node,
10506            scope,
10507        );
10508        true
10509    }
10510
10511    /// Retain the grammar's field interpretation with the original invocation
10512    /// or aggregate context. The replacement AST owns the type, while only the
10513    /// original source owns activation, guards, and occurrence ranges.
10514    fn capture_recovered_field_facts(
10515        &mut self,
10516        unit: &CodeUnit,
10517        range: Range,
10518        context: Node<'_>,
10519        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10520        names_function_type: bool,
10521    ) {
10522        let declaration = *self.source_links[unit]
10523            .last()
10524            .expect("recovered field follows its source occurrence");
10525        let mut fact = crate::source_facts::capture_declaration(
10526            declaration,
10527            context,
10528            unit,
10529            self.source,
10530            self.primary_ancestry,
10531        );
10532        fact.field_type = field_type;
10533        fact.names_function_type = names_function_type;
10534        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10535        let captured = crate::source_context::capture_declaration_context(
10536            context,
10537            unit,
10538            self.source,
10539            &mut self.preprocessor_context_cache,
10540        );
10541        fact.guard_requirements = captured.guard_requirements;
10542        fact.callable_guards = fact.guard_requirements.clone();
10543        fact.callable_activation = Some(range.end_byte);
10544        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10545        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10546        let intern = |node: Node<'_>| {
10547            let mut collector = self.source_collector.borrow_mut();
10548            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10549                collector.intern_node(node)
10550            } else {
10551                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10552            }
10553        };
10554        fact.conditional_family =
10555            crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10556        fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10557        fact.displaced_namespace_closing_brace =
10558            captured.displaced_namespace_closing_brace.map(intern);
10559        self.capture_source_dependencies(&mut fact, context, unit);
10560        self.apply_active_source_context(&mut fact);
10561        assert!(
10562            self.source_properties
10563                .borrow_mut()
10564                .insert(declaration, fact)
10565                .is_none()
10566        );
10567    }
10568
10569    fn materialize_object_macro_fields(
10570        &mut self,
10571        fields: Vec<MacroReplacementField>,
10572        range: Range,
10573        context: Node<'_>,
10574        scope: &ScopeInfo,
10575    ) {
10576        let Some(owner) = scope.class_unit.as_ref() else {
10577            return;
10578        };
10579        for field in fields {
10580            let signature = field.declaration.clone();
10581            let mut fq = owner.fq().clone();
10582            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10583            let short_name = if owner.short_name().is_empty() {
10584                field.name.clone()
10585            } else {
10586                format!("{}.{}", owner.short_name(), field.name)
10587            };
10588            let code_unit = CodeUnit::with_signature_and_fq(
10589                self.file.clone(),
10590                CodeUnitType::Field,
10591                owner.package_name().to_string(),
10592                short_name,
10593                Some(field.declaration),
10594                true,
10595                fq,
10596            );
10597            if self.parsed.contains_declaration(&code_unit) {
10598                continue;
10599            }
10600            self.add_declaration_with_range(
10601                self.primary_ancestry,
10602                code_unit.clone(),
10603                range,
10604                None,
10605                Some(owner.clone()),
10606                None,
10607            );
10608            self.capture_recovered_field_facts(
10609                &code_unit,
10610                range,
10611                context,
10612                field.field_type,
10613                field.names_function_type,
10614            );
10615            self.parsed.add_signature(code_unit, signature);
10616        }
10617    }
10618
10619    fn collapsed_aggregate_owner(
10620        &self,
10621        name: &str,
10622        scope: &ScopeInfo,
10623        parent: Option<&CodeUnit>,
10624    ) -> CodeUnit {
10625        let short_name = parent.map_or_else(
10626            || name.to_string(),
10627            |parent| cpp_join_nested_short(parent.short_name(), name),
10628        );
10629        let fq = cpp_leaf_fq(
10630            &scope.package_name,
10631            parent,
10632            name,
10633            SegmentKind::Nested,
10634            SegmentKind::Type,
10635        );
10636        CodeUnit::with_signature_and_fq(
10637            self.file.clone(),
10638            CodeUnitType::Class,
10639            scope.package_name.clone(),
10640            short_name,
10641            None,
10642            false,
10643            fq,
10644        )
10645    }
10646
10647    /// Recover ordinary aggregates whose field-list macro invocations made
10648    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
10649    /// The class markers, aggregate braces, field nodes, and macro identifier
10650    /// nodes are all read from the AST; no source delimiter or token scan is
10651    /// used. A close-brace node inside a malformed field closes the innermost
10652    /// recovered aggregate, preserving nested-owner identity.
10653    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10654        let mut cursor = node.walk();
10655        let children = node.children(&mut cursor).collect::<Vec<_>>();
10656        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10657        let mut object_macro_fields = self.object_macro_fields.clone();
10658        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10659        let mut open = Vec::<usize>::new();
10660        let mut index = 0;
10661        while index < children.len() {
10662            let keyword = children[index];
10663            if update_object_macro_field_environment(
10664                keyword,
10665                self.source,
10666                &mut object_macro_fields,
10667                &mut ambiguous_object_macro_fields,
10668            ) {
10669                index += 1;
10670                continue;
10671            }
10672            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10673                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10674                if !name.is_empty() {
10675                    let parent = open
10676                        .last()
10677                        .and_then(|class| recovered.get(*class))
10678                        .map(|(owner, _, _, _)| owner.clone())
10679                        .or_else(|| scope.class_unit.clone());
10680                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10681                    recovered.push((
10682                        owner,
10683                        head.key.start_byte(),
10684                        head.opening.end_byte(),
10685                        Vec::new(),
10686                    ));
10687                    let class_index = recovered.len() - 1;
10688                    match head.folded_members {
10689                        // The ordinary shape leaves the aggregate open: every
10690                        // child that follows belongs to it until a close brace
10691                        // ends it.
10692                        None => open.push(class_index),
10693                        // A folded head carries its own member list and its own
10694                        // closing brace, so it closes here. The invocations that
10695                        // precede the class key are the dangling tail of the
10696                        // replacement that folded it, and belong to whatever
10697                        // aggregate is open around it, not to this one.
10698                        Some(members) => {
10699                            let macro_nodes = object_macro_identifier_nodes_with_environment(
10700                                children[index],
10701                                self.source,
10702                                &mut object_macro_fields,
10703                                &mut ambiguous_object_macro_fields,
10704                            );
10705                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10706                                .iter()
10707                                .partition(|node| node.start_byte() < head.key.start_byte());
10708                            if let Some(&enclosing) = open.last() {
10709                                for macro_node in preceding {
10710                                    recovered[enclosing]
10711                                        .3
10712                                        .push(CppCollapsedMember::MacroFields {
10713                                            context: macro_node,
10714                                            range: cpp_declaration_range(macro_node),
10715                                            fields: object_macro_field_closure(
10716                                                &object_macro_fields,
10717                                                &normalize_cpp_whitespace(node_text(
10718                                                    macro_node,
10719                                                    self.source,
10720                                                )),
10721                                            ),
10722                                        });
10723                                }
10724                            }
10725                            let closing = cpp_collapsed_aggregate_closing_brace(members);
10726                            recovered[class_index]
10727                                .3
10728                                .extend(cpp_collapsed_aggregate_members(
10729                                    members,
10730                                    &inner,
10731                                    head.opening.end_byte()..closing,
10732                                    head.opening.end_position().row + 1,
10733                                    self.source,
10734                                    &object_macro_fields,
10735                                ));
10736                            recovered[class_index].2 = members.end_byte();
10737                            for &open_class in &open {
10738                                recovered[open_class].2 =
10739                                    recovered[open_class].2.max(members.end_byte());
10740                            }
10741                        }
10742                    }
10743                    index += head.width;
10744                    continue;
10745                }
10746            }
10747            if let Some(&class_index) = open.last()
10748                && children[index].kind() == "field_declaration"
10749            {
10750                let field = children[index];
10751                let macro_nodes = object_macro_identifier_nodes_with_environment(
10752                    field,
10753                    self.source,
10754                    &mut object_macro_fields,
10755                    &mut ambiguous_object_macro_fields,
10756                );
10757                recovered[class_index]
10758                    .3
10759                    .extend(cpp_collapsed_aggregate_members(
10760                        field,
10761                        &macro_nodes,
10762                        field.start_byte()..field.end_byte(),
10763                        field.start_position().row + 1,
10764                        self.source,
10765                        &object_macro_fields,
10766                    ));
10767                let end = field.end_byte();
10768                for &open_class in &open {
10769                    recovered[open_class].2 = recovered[open_class].2.max(end);
10770                }
10771                let closes = count_close_brace_nodes(field);
10772                for _ in 0..closes {
10773                    if let Some(closed) = open.pop() {
10774                        recovered[closed].2 = end;
10775                    }
10776                }
10777            }
10778            index += 1;
10779        }
10780
10781        let mut owners = Vec::with_capacity(recovered.len());
10782        for (owner, start, end, members) in recovered {
10783            let parent = owners
10784                .iter()
10785                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
10786                .cloned()
10787                .or_else(|| scope.class_unit.clone());
10788            self.declare_collapsed_aggregate(
10789                owner.clone(),
10790                start..end,
10791                Some(node),
10792                parent,
10793                members,
10794                scope,
10795            );
10796            owners.push(owner);
10797        }
10798    }
10799
10800    /// Record one recovered aggregate and the members it declares.
10801    fn declare_collapsed_aggregate(
10802        &mut self,
10803        owner: CodeUnit,
10804        span: std::ops::Range<usize>,
10805        evidence: Option<Node<'_>>,
10806        parent: Option<CodeUnit>,
10807        members: Vec<CppCollapsedMember<'_>>,
10808        scope: &ScopeInfo,
10809    ) {
10810        let range = Range {
10811            start_byte: span.start,
10812            end_byte: span.end,
10813            start_line: self.source.get(..span.start).map_or(1, |source| {
10814                source.bytes().filter(|byte| *byte == b'\n').count() + 1
10815            }),
10816            end_line: self.source.get(..span.end).map_or(1, |source| {
10817                source.bytes().filter(|byte| *byte == b'\n').count() + 1
10818            }),
10819        };
10820        // The tag can already be declared by a typedef or a forward
10821        // declaration, and this range is the definition's. Record it either
10822        // way: a declaration carries every range it is written at, and
10823        // dropping this one leaves nothing able to prove that the aggregate
10824        // has a body (#3098). Recording a range that is already held, or a
10825        // unit that is already declared, changes nothing.
10826        self.add_declaration_with_range(
10827            self.primary_ancestry,
10828            owner.clone(),
10829            range,
10830            evidence,
10831            parent,
10832            None,
10833        );
10834        // The recovery admitted this owner from an aggregate head and body.
10835        // Its malformed outer node need not itself be a struct_specifier, so
10836        // retain that structured definition proof explicitly.
10837        let declaration = *self.source_links[&owner]
10838            .last()
10839            .expect("collapsed aggregate has a source occurrence");
10840        {
10841            let mut properties = self.source_properties.borrow_mut();
10842            let fact = properties
10843                .get_mut(&declaration)
10844                .expect("collapsed aggregate retains its original context");
10845            fact.class_strength =
10846                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
10847            fact.occurrence_role =
10848                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
10849        }
10850        let owner_scope = ScopeInfo {
10851            class_unit: Some(owner),
10852            declarations_are_fields: true,
10853            ..scope.clone()
10854        };
10855        for member in members {
10856            match member {
10857                CppCollapsedMember::MacroFields {
10858                    range,
10859                    fields,
10860                    context,
10861                } => {
10862                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
10863                }
10864                CppCollapsedMember::Declarations {
10865                    span,
10866                    start_line,
10867                    context,
10868                } => {
10869                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
10870                }
10871            }
10872        }
10873    }
10874
10875    /// Recover an aggregate whose head tree-sitter folded into the declaration
10876    /// that precedes it, outside any collapsed `ERROR` container.
10877    ///
10878    /// A comment inside a field-list macro's replacement ends the replacement
10879    /// token, so the rest of the replacement is read at file scope. When the
10880    /// aggregate that follows declares its member list with one invocation,
10881    /// the parser reads the dangling tail as that declaration's type, the
10882    /// class key as an `ERROR`, and the member list as an initializer (#3098).
10883    /// The invocations before the class key belong to the replacement, not to
10884    /// this aggregate, so they go to whatever aggregate encloses it.
10885    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10886        if node.parent().is_some_and(|parent| parent.is_error()) {
10887            // The container's own recovery reads this child in source order,
10888            // with the preprocessor environment its earlier children build.
10889            return false;
10890        }
10891        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
10892            return false;
10893        };
10894        let members = head
10895            .folded_members
10896            .expect("a folded aggregate head carries its member list");
10897        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10898        if name.is_empty() {
10899            return false;
10900        }
10901        let macro_nodes =
10902            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10903        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10904            .iter()
10905            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
10906        for macro_node in preceding {
10907            let fields = object_macro_field_closure(
10908                &self.object_macro_fields,
10909                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
10910            );
10911            self.materialize_object_macro_fields(
10912                fields,
10913                cpp_declaration_range(macro_node),
10914                macro_node,
10915                scope,
10916            );
10917        }
10918        let parent = scope.class_unit.clone();
10919        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10920        let recovered = cpp_collapsed_aggregate_members(
10921            members,
10922            &inner,
10923            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
10924            head.opening.end_position().row + 1,
10925            self.source,
10926            &self.object_macro_fields,
10927        );
10928        self.declare_collapsed_aggregate(
10929            owner,
10930            head.key.start_byte()..members.end_byte(),
10931            Some(node),
10932            parent,
10933            recovered,
10934            scope,
10935        );
10936        true
10937    }
10938
10939    /// Record the members of a collapsed aggregate region as fields of its
10940    /// recovered owner. The names, declaration text, and byte ranges all come
10941    /// from the grammar's reading of the region, so a region that is not a
10942    /// member list contributes nothing.
10943    fn record_collapsed_aggregate_fields(
10944        &mut self,
10945        span: std::ops::Range<usize>,
10946        start_line: usize,
10947        context: Node<'_>,
10948        scope: &ScopeInfo,
10949    ) {
10950        let Some(owner) = scope.class_unit.as_ref() else {
10951            return;
10952        };
10953        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
10954            let range = Range {
10955                start_byte: field.range.start,
10956                end_byte: field.range.end,
10957                start_line: start_line
10958                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
10959                end_line: start_line
10960                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
10961            };
10962            let mut fq = owner.fq().clone();
10963            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10964            let short_name = if owner.short_name().is_empty() {
10965                field.name.clone()
10966            } else {
10967                format!("{}.{}", owner.short_name(), field.name)
10968            };
10969            let signature = normalize_cpp_whitespace(&field.declaration);
10970            let code_unit = CodeUnit::with_signature_and_fq(
10971                self.file.clone(),
10972                CodeUnitType::Field,
10973                owner.package_name().to_string(),
10974                short_name,
10975                Some(signature.clone()),
10976                false,
10977                fq,
10978            );
10979            if self.parsed.contains_declaration(&code_unit) {
10980                continue;
10981            }
10982            self.add_declaration_with_range(
10983                self.primary_ancestry,
10984                code_unit.clone(),
10985                range,
10986                None,
10987                Some(owner.clone()),
10988                None,
10989            );
10990            self.capture_recovered_field_facts(
10991                &code_unit,
10992                range,
10993                context,
10994                field.field_type,
10995                field.names_function_type,
10996            );
10997            self.parsed.add_signature(code_unit, signature);
10998        }
10999    }
11000
11001    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
11002        let Some(_directive) = node.child_by_field_name("directive") else {
11003            return;
11004        };
11005        if is_cpp_undef_directive(node, self.source) {
11006            update_object_macro_field_environment(
11007                node,
11008                self.source,
11009                &mut self.object_macro_fields,
11010                &mut self.ambiguous_object_macro_fields,
11011            );
11012            return;
11013        }
11014        let directly_in_field_list = node
11015            .parent()
11016            .is_some_and(|parent| parent.kind() == "field_declaration_list");
11017        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
11018            self.visit_object_macro_fields(node, scope);
11019        }
11020    }
11021}
11022
11023/// Every member a field-list macro contributes under `environment`, including
11024/// the members of the field-list macros it composes.
11025///
11026/// Composition is resolved at the invocation, not at the definition, because a
11027/// nested name's active replacement is a property of the environment the
11028/// invocation sees. The traversal is iterative and visits each name once, so a
11029/// self-composing or mutually composing pair terminates.
11030fn object_macro_field_closure(
11031    environment: &HashMap<String, ObjectMacroReplacement>,
11032    name: &str,
11033) -> Vec<MacroReplacementField> {
11034    let mut fields = Vec::new();
11035    let mut visited = HashSet::default();
11036    let mut stack = vec![name.to_string()];
11037    while let Some(current) = stack.pop() {
11038        if !visited.insert(current.clone()) {
11039            continue;
11040        }
11041        let Some(replacement) = environment.get(&current) else {
11042            continue;
11043        };
11044        fields.extend(replacement.fields.iter().cloned());
11045        stack.extend(replacement.nested.iter().rev().cloned());
11046    }
11047    fields
11048}
11049
11050/// The structured replacement an object-like `#define` contributes.
11051///
11052/// The directive's whole logical line is the replacement list, which
11053/// [`object_macro_replacement_span`] recovers, because a comment inside the
11054/// replacement ends tree-sitter's `preproc_arg` token early.
11055fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11056    crate::graph::syntax::object_macro_replacement_span(node, source)
11057        .and_then(|span| source.get(span))
11058        .map(crate::graph::syntax::object_macro_replacement)
11059        .unwrap_or_default()
11060}
11061
11062/// Apply one preprocessor directive to an object-like field-list environment.
11063/// The environment is intentionally separate from the declaration visitor so
11064/// malformed parser regions can replay directives in source order without
11065/// mutating the live walk's state ahead of those directives.
11066fn update_object_macro_field_environment(
11067    node: Node<'_>,
11068    source: &str,
11069    fields: &mut HashMap<String, ObjectMacroReplacement>,
11070    ambiguous: &mut HashSet<String>,
11071) -> bool {
11072    match node.kind() {
11073        "preproc_def" => {
11074            let Some(name) = extract_macro_name(node, source) else {
11075                return false;
11076            };
11077            let replacement = object_macro_replacement_of(node, source);
11078            if replacement.is_empty() || ambiguous.contains(&name) {
11079                fields.remove(&name);
11080                ambiguous.insert(name);
11081            } else if let Some(previous) = fields.get(&name) {
11082                if previous != &replacement {
11083                    fields.remove(&name);
11084                    ambiguous.insert(name);
11085                }
11086            } else {
11087                fields.insert(name, replacement);
11088            }
11089            true
11090        }
11091        "preproc_call" if is_cpp_undef_directive(node, source) => {
11092            if let Some(argument) = node.child_by_field_name("argument") {
11093                let name = node_text(argument, source).trim();
11094                fields.remove(name);
11095                if inside_preprocessor_conditional(node) {
11096                    ambiguous.insert(name.to_string());
11097                } else {
11098                    ambiguous.remove(name);
11099                }
11100            }
11101            true
11102        }
11103        _ => false,
11104    }
11105}
11106
11107fn object_macro_identifier_nodes<'tree>(
11108    node: Node<'tree>,
11109    source: &str,
11110    fields: &HashMap<String, ObjectMacroReplacement>,
11111) -> Vec<Node<'tree>> {
11112    walk_object_macro_identifier_nodes(node, |current, result| {
11113        if matches!(
11114            current.kind(),
11115            "identifier" | "field_identifier" | "type_identifier"
11116        ) && fields.contains_key(node_text(current, source).trim())
11117        {
11118            result.push(current);
11119        }
11120        false
11121    })
11122}
11123
11124fn walk_object_macro_identifier_nodes<'tree>(
11125    node: Node<'tree>,
11126    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11127) -> Vec<Node<'tree>> {
11128    let mut result = Vec::new();
11129    let mut stack = vec![node];
11130    while let Some(current) = stack.pop() {
11131        if visit(current, &mut result) {
11132            continue;
11133        }
11134        let mut cursor = current.walk();
11135        let children = current.children(&mut cursor).collect::<Vec<_>>();
11136        stack.extend(children.into_iter().rev());
11137    }
11138    result.sort_by_key(|node| node.start_byte());
11139    result
11140}
11141
11142/// Collect macro identifiers while replaying any nested preprocessor
11143/// directives in source order. Recovery regions can contain a later `#undef`
11144/// in the same parser error envelope; using the live visitor map for the
11145/// entire envelope would incorrectly materialize invocations after it.
11146fn object_macro_identifier_nodes_with_environment<'tree>(
11147    node: Node<'tree>,
11148    source: &str,
11149    fields: &mut HashMap<String, ObjectMacroReplacement>,
11150    ambiguous: &mut HashSet<String>,
11151) -> Vec<Node<'tree>> {
11152    walk_object_macro_identifier_nodes(node, |current, result| {
11153        if update_object_macro_field_environment(current, source, fields, ambiguous) {
11154            return true;
11155        }
11156        if matches!(
11157            current.kind(),
11158            "identifier" | "field_identifier" | "type_identifier"
11159        ) && fields.contains_key(node_text(current, source).trim())
11160        {
11161            result.push(current);
11162        }
11163        false
11164    })
11165}
11166
11167/// One member region of an aggregate whose head and body tree-sitter collapsed
11168/// into an `ERROR` container, in source order.
11169enum CppCollapsedMember<'tree> {
11170    /// The members a field-list macro invocation donates, all sharing the
11171    /// invocation's range.
11172    MacroFields {
11173        context: Node<'tree>,
11174        range: Range,
11175        fields: Vec<MacroReplacementField>,
11176    },
11177    /// A byte span whose ordinary member declarations lost their shape with
11178    /// the aggregate body, with the 1-based line the span starts on.
11179    Declarations {
11180        context: Node<'tree>,
11181        span: std::ops::Range<usize>,
11182        start_line: usize,
11183    },
11184}
11185
11186/// One aggregate head recovered from the children of a collapsed `ERROR`
11187/// container.
11188///
11189/// A member list that opens with an object-like field-list macro invocation
11190/// has no grammar rule, so the aggregate never forms a specifier and its head
11191/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
11192/// class key, the name, and the opening brace are three consecutive children.
11193/// When the aggregate follows the dangling tail of a comment-truncated
11194/// replacement, the parser reads that tail as the declaration's type and folds
11195/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
11196/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
11197/// }` that follows becomes one `field_declaration` whose class key is an
11198/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
11199/// `initializer_list` (#3098).
11200struct CppCollapsedAggregateHead<'tree> {
11201    key: Node<'tree>,
11202    name: Node<'tree>,
11203    opening: Node<'tree>,
11204    /// The member list when the head folded it into its own node, which also
11205    /// carries the aggregate's closing brace. `None` leaves the aggregate open
11206    /// for the children that follow it.
11207    folded_members: Option<Node<'tree>>,
11208    /// How many children the head consumes.
11209    width: usize,
11210}
11211
11212/// The aggregate head that starts at `children[index]`, if one does.
11213fn cpp_collapsed_aggregate_head<'tree>(
11214    children: &[Node<'tree>],
11215    index: usize,
11216    source: &str,
11217) -> Option<CppCollapsedAggregateHead<'tree>> {
11218    let key = *children.get(index)?;
11219    if matches!(key.kind(), "struct" | "class" | "union") {
11220        let name = *children.get(index + 1)?;
11221        let opening = *children.get(index + 2)?;
11222        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11223            return None;
11224        }
11225        return Some(CppCollapsedAggregateHead {
11226            key,
11227            name,
11228            opening,
11229            folded_members: None,
11230            width: 3,
11231        });
11232    }
11233    cpp_folded_aggregate_head(key, source)
11234}
11235
11236/// The aggregate head tree-sitter folded into one declaration node, reading
11237/// the class key as an `ERROR` and the member list as an initializer.
11238fn cpp_folded_aggregate_head<'tree>(
11239    node: Node<'tree>,
11240    source: &str,
11241) -> Option<CppCollapsedAggregateHead<'tree>> {
11242    if !matches!(node.kind(), "declaration" | "field_declaration") {
11243        return None;
11244    }
11245    let mut cursor = node.walk();
11246    let children = node.children(&mut cursor).collect::<Vec<_>>();
11247    let key_index = children.iter().position(|child| {
11248        child.is_error()
11249            && matches!(
11250                node_text(*child, source).trim(),
11251                "struct" | "class" | "union"
11252            )
11253    })?;
11254    let declarator = *children.get(key_index + 1)?;
11255    // At file scope the grammar has an `init_declarator` for the name and its
11256    // initializer; inside an `ERROR` container the two are loose siblings.
11257    let (name, members) = if declarator.kind() == "init_declarator" {
11258        (
11259            declarator.child_by_field_name("declarator")?,
11260            declarator.child_by_field_name("value")?,
11261        )
11262    } else {
11263        (declarator, *children.get(key_index + 2)?)
11264    };
11265    if !matches!(
11266        name.kind(),
11267        "field_identifier" | "type_identifier" | "identifier"
11268    ) {
11269        return None;
11270    }
11271    if members.kind() != "initializer_list" {
11272        return None;
11273    }
11274    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11275    Some(CppCollapsedAggregateHead {
11276        key: children[key_index],
11277        name,
11278        opening,
11279        folded_members: Some(members),
11280        width: 1,
11281    })
11282}
11283
11284/// Where a folded member list ends: at its own closing brace when it has one,
11285/// so the reparse of the declarations it holds sees a member list and not a
11286/// stray brace.
11287fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11288    let mut cursor = members.walk();
11289    members
11290        .children(&mut cursor)
11291        .filter(|child| child.kind() == "}" && !child.is_missing())
11292        .last()
11293        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11294}
11295
11296/// The members one collapsed aggregate region declares, in source order: what
11297/// its field-list macro invocations donate, then the ordinary declarations
11298/// that follow the last invocation and lost their shape with the body.
11299fn cpp_collapsed_aggregate_members<'tree>(
11300    context: Node<'tree>,
11301    macro_nodes: &[Node<'tree>],
11302    region: std::ops::Range<usize>,
11303    region_start_line: usize,
11304    source: &str,
11305    environment: &HashMap<String, ObjectMacroReplacement>,
11306) -> Vec<CppCollapsedMember<'tree>> {
11307    let mut members = macro_nodes
11308        .iter()
11309        .map(|macro_node| CppCollapsedMember::MacroFields {
11310            context: *macro_node,
11311            range: cpp_declaration_range(*macro_node),
11312            fields: object_macro_field_closure(
11313                environment,
11314                &normalize_cpp_whitespace(node_text(*macro_node, source)),
11315            ),
11316        })
11317        .collect::<Vec<_>>();
11318    // Whatever follows the last invocation is an ordinary member list that
11319    // lost its declaration shape with the aggregate's body; the grammar
11320    // recovers it from its own byte range.
11321    let declarations_start = macro_nodes
11322        .last()
11323        .map_or(region.start, |macro_node| macro_node.end_byte());
11324    members.push(CppCollapsedMember::Declarations {
11325        context,
11326        span: declarations_start..region.end,
11327        start_line: region_start_line
11328            + cpp_line_breaks_between(source, region.start, declarations_start),
11329    });
11330    members
11331}
11332
11333/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
11334/// line without rescanning the file from its start.
11335fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11336    source.get(from..to).map_or(0, |slice| {
11337        slice.bytes().filter(|byte| *byte == b'\n').count()
11338    })
11339}
11340
11341/// How many enclosing aggregates a collapsed member declaration closes: the
11342/// close braces it carries that its own braces do not balance. A member that
11343/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
11344/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
11345/// owner early and lost every member after it (#2985).
11346fn count_close_brace_nodes(node: Node<'_>) -> usize {
11347    let mut opened = 0usize;
11348    let mut closed = 0usize;
11349    let mut stack = vec![node];
11350    while let Some(current) = stack.pop() {
11351        if !current.is_missing() {
11352            match current.kind() {
11353                "{" => opened += 1,
11354                "}" => closed += 1,
11355                _ => {}
11356            }
11357        }
11358        let mut cursor = current.walk();
11359        stack.extend(current.children(&mut cursor));
11360    }
11361    closed.saturating_sub(opened)
11362}
11363
11364/// Classify a C++ field while its declaration syntax is already available.
11365///
11366/// The persisted result lets later visibility queries avoid reparsing the
11367/// complete source file only to recover linkage.
11368pub fn cpp_field_declaration_linkage<'tree>(
11369    declaration: Node<'tree>,
11370    source: &str,
11371    ancestry: &ParentIndex<'tree>,
11372) -> CppFieldLinkage {
11373    let mut current = ancestry.parent(declaration);
11374    let mut enclosed_by_class = false;
11375    while let Some(node) = current {
11376        if node.kind() == "namespace_definition"
11377            && node
11378                .child_by_field_name("name")
11379                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11380        {
11381            return CppFieldLinkage::Internal;
11382        }
11383        if matches!(
11384            node.kind(),
11385            "class_specifier" | "struct_specifier" | "union_specifier"
11386        ) && node
11387            .child_by_field_name("name")
11388            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11389        {
11390            return CppFieldLinkage::Internal;
11391        }
11392        if matches!(
11393            node.kind(),
11394            "class_specifier" | "struct_specifier" | "union_specifier"
11395        ) {
11396            enclosed_by_class = true;
11397        }
11398        if matches!(node.kind(), "function_definition" | "lambda_expression") {
11399            return CppFieldLinkage::Internal;
11400        }
11401        current = ancestry.parent(node);
11402    }
11403    if enclosed_by_class {
11404        return CppFieldLinkage::External;
11405    }
11406    let mut cursor = declaration.walk();
11407    let mut has_static = false;
11408    let mut has_extern = false;
11409    let mut has_inline = false;
11410    let mut has_const = false;
11411    let mut has_constexpr = false;
11412    for child in declaration.named_children(&mut cursor) {
11413        let text = normalize_cpp_whitespace(node_text(child, source));
11414        match (child.kind(), text.as_str()) {
11415            ("storage_class_specifier", "static") => has_static = true,
11416            ("storage_class_specifier", "extern") => has_extern = true,
11417            ("storage_class_specifier", "inline") => has_inline = true,
11418            ("storage_class_specifier", "constexpr") => has_constexpr = true,
11419            ("type_qualifier", "const") => has_const = true,
11420            ("type_qualifier", "constexpr") => has_constexpr = true,
11421            _ => {}
11422        }
11423    }
11424    if has_static {
11425        CppFieldLinkage::Internal
11426    } else if has_extern || has_inline {
11427        CppFieldLinkage::External
11428    } else if has_const || has_constexpr {
11429        CppFieldLinkage::InternalUnlessExternalPeer
11430    } else {
11431        CppFieldLinkage::External
11432    }
11433}
11434
11435fn cpp_declaration_range(node: Node<'_>) -> Range {
11436    Range {
11437        start_byte: node.start_byte(),
11438        end_byte: node.end_byte(),
11439        start_line: node.start_position().row + 1,
11440        end_line: node.end_position().row + 1,
11441    }
11442}
11443
11444/// A recovery interval as a [`Range`], for materialization records whose
11445/// window is a byte region rather than one parser node (the sentinel-macro
11446/// region reparses, issue #941/#1657).
11447fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11448    let line_at = |byte: usize| {
11449        source.as_bytes()[..byte]
11450            .iter()
11451            .filter(|&&b| b == b'\n')
11452            .count()
11453            + 1
11454    };
11455    Range {
11456        start_byte,
11457        end_byte,
11458        start_line: line_at(start_byte),
11459        end_line: line_at(end_byte),
11460    }
11461}
11462
11463fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11464    ImportInfo {
11465        raw_snippet,
11466        is_wildcard: false,
11467        is_global: false,
11468        identifier: None,
11469        alias: None,
11470        path: None,
11471        binder_span: None,
11472    }
11473}
11474
11475fn recover_quoted_source_imports(
11476    source: &str,
11477    imports: &mut Vec<SourceImportFact>,
11478    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11479    collector: &RefCell<PrimarySourceFactCollector<'_>>,
11480) {
11481    let mut in_block_comment = false;
11482    let mut start = 0;
11483    for line in source.split_inclusive('\n') {
11484        let end = start + line.len();
11485        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11486        let trimmed = stripped.trim();
11487        if looks_like_quoted_include_line(trimmed) {
11488            let raw = normalize_cpp_whitespace(trimmed);
11489            if !imports.iter().any(|import| import.statement == raw) {
11490                let occurrence = collector.borrow_mut().intern_subspan_bytes(
11491                    start,
11492                    end,
11493                    SourceOccurrenceProvenance::ExplicitSubspan,
11494                );
11495                // Recovery already identified a quoted directive hidden by a
11496                // malformed outer construct. Interpret its path with the same
11497                // grammar in an isolated included range, preserving byte identity.
11498                let region =
11499                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11500                let target = region.as_ref().and_then(|tree| {
11501                    let mut pending = vec![tree.root_node()];
11502                    while let Some(node) = pending.pop() {
11503                        if node.kind() == "preproc_include"
11504                            && let Some(path) = node.child_by_field_name("path")
11505                            && path.kind() == "string_literal"
11506                        {
11507                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
11508                            let target = collector.borrow_mut().intern_embedded(
11509                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11510                            );
11511                            includes.push(
11512                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11513                                    declaration: occurrence,
11514                                    target,
11515                                    path: value.to_owned(),
11516                                    quoted: true,
11517                                },
11518                            );
11519                            return Some(target);
11520                        }
11521                        let mut cursor = node.walk();
11522                        let children: Vec<_> = node.named_children(&mut cursor).collect();
11523                        pending.extend(children.into_iter().rev());
11524                    }
11525                    None
11526                });
11527                imports.push(SourceImportFact::from_import(
11528                    cpp_include_info(raw),
11529                    occurrence,
11530                    target,
11531                    None,
11532                    Vec::new(),
11533                ));
11534            }
11535        }
11536        start = end;
11537    }
11538}
11539
11540fn looks_like_quoted_include_line(line: &str) -> bool {
11541    let Some(rest) = line.trim_start().strip_prefix('#') else {
11542        return false;
11543    };
11544    let Some(rest) = rest.trim_start().strip_prefix("include") else {
11545        return false;
11546    };
11547    rest.trim_start().starts_with('"')
11548}
11549
11550fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11551    let mut raw = Vec::new();
11552    if let Some(bases) = cpp_class_like_base_clause(node) {
11553        collect_cpp_base_nodes(bases, source, &mut raw);
11554    }
11555    raw
11556}
11557
11558fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11559    let mut facts = Vec::new();
11560    let mut cursor = node.walk();
11561    for child in node.named_children(&mut cursor) {
11562        if child.kind() != "base_class_clause" {
11563            continue;
11564        }
11565        let mut is_virtual = false;
11566        let mut child_cursor = child.walk();
11567        for base in child.children(&mut child_cursor) {
11568            match base.kind() {
11569                "virtual" => is_virtual = true,
11570                "," => is_virtual = false,
11571                "type_identifier"
11572                | "qualified_identifier"
11573                | "scoped_type_identifier"
11574                | "template_type"
11575                | "template_function" => {
11576                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
11577                        base,
11578                        source,
11579                        is_virtual,
11580                        crate::source_facts::base_access_specifier(child, base, node.kind()),
11581                    ) {
11582                        facts.push(fact);
11583                    }
11584                    is_virtual = false;
11585                }
11586                _ => {}
11587            }
11588        }
11589    }
11590    facts
11591}
11592
11593/// The `base_class_clause` a class-like node writes, if it writes one.
11594///
11595/// The pinned grammar exposes the clause as a named child rather than a field,
11596/// so the question is asked by kind. A base clause is the only place a C++
11597/// class body records that some other class may declare a member, which is
11598/// what [`cpp_callable_dispatch_extensibility`] reads it for.
11599fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11600    let mut cursor = class_like.walk();
11601    class_like
11602        .named_children(&mut cursor)
11603        .find(|child| child.kind() == "base_class_clause")
11604}
11605
11606fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11607    walk_named_tree_preorder(node, false, |child| match child.kind() {
11608        "type_identifier" | "qualified_identifier" | "template_type" => {
11609            let text = normalize_cpp_whitespace(node_text(child, source));
11610            if !text.is_empty() {
11611                raw.push(text);
11612            }
11613            WalkControl::SkipChildren
11614        }
11615        _ => WalkControl::Continue,
11616    });
11617}
11618
11619fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11620    let mut out = String::new();
11621    let chars: Vec<char> = line.chars().collect();
11622    let mut index = 0;
11623    let mut in_string = false;
11624    let mut in_char = false;
11625    let mut escape = false;
11626
11627    while index < chars.len() {
11628        let ch = chars[index];
11629        let next = chars.get(index + 1).copied();
11630
11631        if *in_block_comment {
11632            if ch == '*' && next == Some('/') {
11633                *in_block_comment = false;
11634                index += 2;
11635            } else {
11636                index += 1;
11637            }
11638            continue;
11639        }
11640
11641        if in_string {
11642            out.push(ch);
11643            if escape {
11644                escape = false;
11645            } else if ch == '\\' {
11646                escape = true;
11647            } else if ch == '"' {
11648                in_string = false;
11649            }
11650            index += 1;
11651            continue;
11652        }
11653
11654        if in_char {
11655            out.push(ch);
11656            if escape {
11657                escape = false;
11658            } else if ch == '\\' {
11659                escape = true;
11660            } else if ch == '\'' {
11661                in_char = false;
11662            }
11663            index += 1;
11664            continue;
11665        }
11666
11667        if ch == '/' && next == Some('/') {
11668            break;
11669        }
11670        if ch == '/' && next == Some('*') {
11671            *in_block_comment = true;
11672            index += 2;
11673            continue;
11674        }
11675        if ch == '"' {
11676            in_string = true;
11677            out.push(ch);
11678            index += 1;
11679            continue;
11680        }
11681        if ch == '\'' {
11682            in_char = true;
11683            out.push(ch);
11684            index += 1;
11685            continue;
11686        }
11687
11688        out.push(ch);
11689        index += 1;
11690    }
11691
11692    out
11693}
11694
11695#[derive(Clone)]
11696struct FunctionInfo {
11697    package_name: String,
11698    owner: Option<CppMemberOwner>,
11699    name: String,
11700    signature: String,
11701}
11702
11703/// Owner of a member function, kept structured so a literal `$` inside a
11704/// source-level class name never crosses a join/split boundary: the legacy
11705/// `$`-joined owner string was re-split at fq construction, dropping a leading
11706/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
11707/// and tripping the package/short boundary assert -- the #2140 corruption one
11708/// level up (#2362).
11709#[derive(Clone)]
11710enum CppMemberOwner {
11711    /// Source-level owner class chain from a qualified declarator-id, one
11712    /// class name per component (`Outer::Inner::method` -> `["Outer",
11713    /// "Inner"]`); each component may itself contain a literal `$`.
11714    Chain(Vec<String>),
11715    /// The lexically enclosing or recovered class unit; the member fq extends
11716    /// its fq directly instead of re-splitting its `$`-joined short chain.
11717    Unit(CodeUnit),
11718}
11719
11720impl CppMemberOwner {
11721    /// The legacy `$`-joined owner chain used in the member's short name.
11722    fn short_chain(&self) -> String {
11723        match self {
11724            Self::Chain(chain) => chain.join("$"),
11725            Self::Unit(parent) => parent.short_name().to_string(),
11726        }
11727    }
11728}
11729
11730enum DeclaratorKind<'a> {
11731    Function(Node<'a>),
11732    Variable(Node<'a>),
11733}
11734
11735impl FunctionInfo {
11736    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
11737        self.code_unit_with_synthetic(file, false)
11738    }
11739
11740    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
11741        let short_name = match &self.owner {
11742            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
11743            None => self.name.clone(),
11744        };
11745        let fq = match &self.owner {
11746            Some(CppMemberOwner::Chain(chain)) => {
11747                debug_assert!(
11748                    !chain.is_empty(),
11749                    "an empty owner chain is no owner; producers return None instead"
11750                );
11751                let mut fq = FqName::new();
11752                cpp_push_package(&mut fq, &self.package_name);
11753                let mut first = true;
11754                for component in chain {
11755                    let kind = if first {
11756                        SegmentKind::Type
11757                    } else {
11758                        SegmentKind::Nested
11759                    };
11760                    fq.push(segment_interner().intern(component, kind));
11761                    first = false;
11762                }
11763                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11764                fq
11765            }
11766            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
11767                .fq()
11768                .clone()
11769                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
11770            // An anonymous parent (empty short chain) contributes no owner
11771            // segment -- the same guard as cpp_join_member_short above.
11772            Some(CppMemberOwner::Unit(_)) | None => {
11773                let mut fq = FqName::new();
11774                cpp_push_package(&mut fq, &self.package_name);
11775                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
11776                fq
11777            }
11778        };
11779        CodeUnit::with_signature_and_fq(
11780            file,
11781            CodeUnitType::Function,
11782            self.package_name.clone(),
11783            short_name,
11784            Some(self.signature.clone()),
11785            synthetic,
11786            fq,
11787        )
11788    }
11789}
11790
11791fn extract_function_info(
11792    declarator: Node<'_>,
11793    source: &str,
11794    scope: &ScopeInfo,
11795) -> Option<FunctionInfo> {
11796    let parameters_node = declarator.child_by_field_name("parameters")?;
11797    let declarator_name_node = declarator
11798        .child_by_field_name("declarator")
11799        .or_else(|| parameters_node.prev_named_sibling())?;
11800    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
11801}
11802
11803fn extract_function_info_from_name(
11804    declarator: Node<'_>,
11805    declarator_name_node: Node<'_>,
11806    source: &str,
11807    scope: &ScopeInfo,
11808) -> Option<FunctionInfo> {
11809    let parameters_node = declarator.child_by_field_name("parameters")?;
11810    let parameters_text = cpp_parameter_signature(parameters_node, source);
11811    let recovered_specialization_member = scope
11812        .recovered_specialization_member_scope
11813        .then(|| {
11814            let terminal = declarator_name_node
11815                .child_by_field_name("name")
11816                .unwrap_or(declarator_name_node);
11817            let name = canonical_cpp_qualified_component(terminal, source)?.name;
11818            let owner = scope.class_unit.as_ref()?;
11819            Some((
11820                Some(CppMemberOwner::Unit(owner.clone())),
11821                name,
11822                scope.package_name.clone(),
11823            ))
11824        })
11825        .flatten();
11826    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
11827        parts
11828    } else if let Some(parts) =
11829        split_structured_templated_cpp_name(declarator_name_node, source, scope)
11830    {
11831        parts
11832    } else {
11833        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
11834            declarator_name_node,
11835            source,
11836        )?);
11837        if raw_name.is_empty() {
11838            return None;
11839        }
11840        split_cpp_name(&raw_name, scope)
11841    };
11842    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
11843    let mut signature = if suffix.is_empty() {
11844        parameters_text
11845    } else {
11846        format!("{parameters_text} {suffix}")
11847    };
11848    if let Some(template_signature) = &scope.template_signature {
11849        signature = format!("{template_signature}{signature}");
11850    }
11851
11852    Some(FunctionInfo {
11853        package_name,
11854        owner,
11855        name,
11856        signature,
11857    })
11858}
11859
11860/// Recover the semantic return type and callable name when a declaration macro
11861/// occupies a function definition's `type` field. Tree-sitter either exposes a
11862/// scalar return as the declarator's apparent name and the callable as the sole
11863/// identifier in an `ERROR`, or joins a template return and callable into a
11864/// qualified identifier with a missing `::`. Both shapes retain the complete
11865/// parameter list and body; a concrete separator remains an out-of-line member.
11866fn cpp_macro_displaced_callable_parts<'tree>(
11867    function_declarator: Node<'tree>,
11868    source: &str,
11869    ancestry: &ParentIndex<'tree>,
11870) -> Option<(Node<'tree>, Node<'tree>)> {
11871    let definition = ancestry.parent(function_declarator)?;
11872    if definition.kind() != "function_definition"
11873        || definition.child_by_field_name("declarator") != Some(function_declarator)
11874        || definition
11875            .child_by_field_name("body")
11876            .is_none_or(|body| body.kind() != "compound_statement")
11877    {
11878        return None;
11879    }
11880    let macro_type = definition.child_by_field_name("type")?;
11881    if macro_type.kind() != "type_identifier"
11882        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11883    {
11884        return None;
11885    }
11886
11887    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
11888    if apparent_return_type.kind() == "qualified_identifier"
11889        && let (Some(return_type), Some(callable_name)) = (
11890            apparent_return_type.child_by_field_name("scope"),
11891            apparent_return_type.child_by_field_name("name"),
11892        )
11893        && return_type.kind() == "template_type"
11894        && matches!(callable_name.kind(), "identifier" | "field_identifier")
11895        && (0..apparent_return_type.child_count())
11896            .filter_map(|index| apparent_return_type.child(index))
11897            .any(|child| child.kind() == "::" && child.is_missing())
11898        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
11899        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
11900    {
11901        return Some((return_type, callable_name));
11902    }
11903    if !matches!(
11904        apparent_return_type.kind(),
11905        "identifier" | "field_identifier" | "type_identifier"
11906    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
11907    {
11908        return None;
11909    }
11910    let parameters = function_declarator.child_by_field_name("parameters")?;
11911    let mut cursor = function_declarator.walk();
11912    let between = function_declarator
11913        .named_children(&mut cursor)
11914        .filter(|child| child.kind() != "comment")
11915        .filter(|child| {
11916            child.start_byte() >= apparent_return_type.end_byte()
11917                && child.end_byte() <= parameters.start_byte()
11918                && !same_node(*child, apparent_return_type)
11919                && !same_node(*child, parameters)
11920        })
11921        .collect::<Vec<_>>();
11922    let [name_error] = between.as_slice() else {
11923        return None;
11924    };
11925    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
11926        return None;
11927    }
11928    let callable_name = name_error.named_child(0)?;
11929    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
11930        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
11931    {
11932        return None;
11933    }
11934    Some((apparent_return_type, callable_name))
11935}
11936
11937/// The part of a `function_declarator` after its parameter list that belongs to
11938/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
11939/// specification, a trailing return type and a trailing requires-clause.
11940///
11941/// The grammar makes each of these a distinct sibling of the `parameters`
11942/// field, so they are read from the tree. Splitting the declarator's text on
11943/// the parameter list instead silently dropped every qualifier whenever the
11944/// parameter list was spelled with whitespace that normalization rewrote - a
11945/// line break or a double space was enough to make a `const` member definition
11946/// a different logical symbol from its declaration (#1827).
11947///
11948/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
11949/// are deliberately excluded. C++ does not make them part of the signature and
11950/// an out-of-line definition never repeats them, so including them would split
11951/// a declaration from its own definition.
11952fn cpp_declarator_identity_suffix(
11953    declarator: Node<'_>,
11954    parameters_node: Node<'_>,
11955    source: &str,
11956) -> String {
11957    let mut cursor = declarator.walk();
11958    let parts = declarator
11959        .named_children(&mut cursor)
11960        .filter(|child| child.start_byte() >= parameters_node.end_byte())
11961        .filter(|child| {
11962            matches!(
11963                child.kind(),
11964                "type_qualifier"
11965                    | "ref_qualifier"
11966                    | "noexcept"
11967                    | "throw_specifier"
11968                    | "trailing_return_type"
11969                    | "requires_clause"
11970            )
11971        })
11972        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
11973        .filter(|text| !text.is_empty())
11974        .collect::<Vec<_>>();
11975    normalize_cpp_qualifier_suffix(&parts.join(" "))
11976}
11977
11978/// The identity suffix of one callable declarator, for a consumer that holds
11979/// the declarator rather than the declaration walk's parts.
11980///
11981/// The persisted signature concatenates the parameter spelling and this suffix,
11982/// so a comparison that must agree on the suffix alone recomputes it here
11983/// instead of splitting the stored string.
11984pub(crate) fn cpp_callable_identity_suffix(
11985    function_declarator: Node<'_>,
11986    source: &str,
11987) -> Option<String> {
11988    let parameters_node = function_declarator.child_by_field_name("parameters")?;
11989    Some(cpp_declarator_identity_suffix(
11990        function_declarator,
11991        parameters_node,
11992        source,
11993    ))
11994}
11995
11996pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
11997    match classify_declarator(node)? {
11998        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
11999        DeclaratorKind::Variable(_) => None,
12000    }
12001}
12002
12003fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12004    match node.kind() {
12005        "function_declarator" => {
12006            let inner = node
12007                .child_by_field_name("declarator")
12008                .or_else(|| node.child_by_field_name("name"))
12009                .or_else(|| last_named_child(node));
12010            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12011                Some(DeclaratorKind::Variable(node))
12012            } else {
12013                Some(DeclaratorKind::Function(node))
12014            }
12015        }
12016        // A macro marker is declaration provenance, never a declarator: its
12017        // identifier names the macro, not anything the declaration declares.
12018        "macro_modifier" => None,
12019        "init_declarator"
12020        | "pointer_declarator"
12021        | "reference_declarator"
12022        | "parenthesized_declarator"
12023        | "array_declarator"
12024        | "attributed_declarator"
12025        | "template_function" => node
12026            .child_by_field_name("declarator")
12027            .or_else(|| node.child_by_field_name("name"))
12028            .or_else(|| last_named_child(node))
12029            .and_then(classify_declarator),
12030        "identifier" | "field_identifier" | "qualified_identifier" => {
12031            Some(DeclaratorKind::Variable(node))
12032        }
12033        _ => node
12034            .child_by_field_name("declarator")
12035            .or_else(|| node.child_by_field_name("name"))
12036            .or_else(|| last_named_child(node))
12037            .and_then(classify_declarator),
12038    }
12039}
12040
12041fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12042    matches!(
12043        node.kind(),
12044        "function_declarator"
12045            | "init_declarator"
12046            | "pointer_declarator"
12047            | "reference_declarator"
12048            | "parenthesized_declarator"
12049            | "array_declarator"
12050            | "attributed_declarator"
12051            | "template_function"
12052            | "macro_modifier_declarator"
12053            | "identifier"
12054            | "field_identifier"
12055            | "qualified_identifier"
12056    )
12057}
12058
12059fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12060    let class_like = first_class_like_child(node);
12061    let mut cursor = node.walk();
12062    node.named_children(&mut cursor).any(|child| {
12063        matches!(
12064            child.kind(),
12065            "init_declarator"
12066                | "pointer_declarator"
12067                | "reference_declarator"
12068                | "array_declarator"
12069                | "function_declarator"
12070                | "parenthesized_declarator"
12071                | "attributed_declarator"
12072        ) || matches!(
12073            child.kind(),
12074            "identifier" | "field_identifier" | "qualified_identifier"
12075        ) && class_like.is_none_or(|class_node| {
12076            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12077        })
12078    })
12079}
12080
12081/// One namespace-scope forward class declaration that a recovered export-macro
12082/// class definition may borrow its identity from.  Tree-sitter can close a
12083/// malformed class at the enclosing namespace's closing brace, leaving the later
12084/// class definitions as root-level recovered `function_definition` nodes.  A
12085/// preceding `class Name;` in the same namespace is the only structured identity
12086/// signal available in that shape.
12087///
12088/// Everything recorded here is a property of the forward declaration alone.
12089/// What depends on the node doing the asking -- that the forward and its
12090/// namespace both close before it, with nothing but recovery trivia between --
12091/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
12092/// tree answers every later question about it.
12093struct CppNamespaceForward {
12094    name: String,
12095    start_byte: usize,
12096    /// Where the malformed namespace that held the forward ended.  No query
12097    /// asks anything else about that node.
12098    namespace_end_byte: usize,
12099    package_name: String,
12100}
12101
12102/// Read `node` as a borrowable namespace forward declaration.
12103///
12104/// The admission is deliberately conservative: only a body-less class specifier
12105/// whose declaration has no declarator, at namespace scope rather than inside a
12106/// function or class body, in a namespace that itself failed to parse.
12107fn cpp_namespace_forward_entry<'tree>(
12108    node: Node<'tree>,
12109    source: &str,
12110    ancestry: &ParentIndex<'tree>,
12111) -> Option<CppNamespaceForward> {
12112    if !matches!(
12113        node.kind(),
12114        "class_specifier" | "struct_specifier" | "union_specifier"
12115    ) || cpp_body_node(node).is_some()
12116    {
12117        return None;
12118    }
12119    let parent = node.parent()?;
12120    if !(parent.kind() == "declaration_list"
12121        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12122    {
12123        return None;
12124    }
12125    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12126    // Borrowing is only justified by the parser-recovery shape we are
12127    // repairing: the namespace that held the forward must itself contain a
12128    // syntax error. A clean, unrelated namespace forward is not an identity
12129    // proof.
12130    if !namespace.has_error() {
12131        return None;
12132    }
12133    Some(CppNamespaceForward {
12134        name: class_like_name(node, source, ancestry)?,
12135        start_byte: node.start_byte(),
12136        namespace_end_byte: namespace.end_byte(),
12137        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12138    })
12139}
12140
12141/// Whether `forward` stands in the recovery relation to the node asking about
12142/// it: it and its malformed namespace both closed before the recovered class,
12143/// and nothing but recovery trivia separates the two.
12144fn cpp_namespace_forward_matches_recovery(
12145    forward: &CppNamespaceForward,
12146    recovered_node: Node<'_>,
12147) -> bool {
12148    forward.start_byte < recovered_node.start_byte()
12149        && forward.namespace_end_byte < recovered_node.start_byte()
12150        && malformed_namespace_is_nearest_recovery_region(
12151            forward.namespace_end_byte,
12152            recovered_node,
12153        )
12154}
12155
12156/// What one open [`CppVisitor::record_recovered_declarations`] has watched
12157/// happen to the declaration set.
12158///
12159/// The recovered set used to be a difference against a clone of the whole
12160/// declaration set, taken once per recovery: O(recoveries x declarations) on
12161/// exactly the error-recovered files that already walk slowest (#2787). The
12162/// walk knows which declarations it creates, so the capture collects them as
12163/// they are made and the difference is never needed.
12164///
12165/// `removed_pre_existing` is what makes that equal to the difference. A
12166/// deferred replacement removes the replaced declaration's children
12167/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
12168/// re-creates them. Creation alone cannot tell that apart from a first mint, so
12169/// a removal of a declaration this capture did not create records that it was
12170/// already there when the capture opened.
12171#[derive(Debug, Default)]
12172pub struct CppRecoveryCapture {
12173    /// Declarations created while this capture was open, in creation order.
12174    created: Vec<CodeUnit>,
12175    /// Membership for `created`.
12176    created_units: HashSet<CodeUnit>,
12177    /// Declarations that predate this capture and have been removed during it.
12178    removed_pre_existing: HashSet<CodeUnit>,
12179}
12180
12181/// Which owners the parse product already holds field declarations for, folded
12182/// in as the walk records them.
12183///
12184/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
12185/// and used to answer it by scanning every declaration accumulated so far:
12186/// O(enums x declarations) on exactly the generated headers that declare many
12187/// of both (#2786). The answer only grows by declaration, so the walk carries
12188/// it. A field's short name names its owner chain, `Owner.member`, so one field
12189/// answers for every dotted prefix of its own short name; an anonymous enum's
12190/// or union's enumerators carry bare short names instead (#2140), which is what
12191/// an empty owner short name asks about.
12192#[derive(Debug, Default)]
12193pub struct CppFieldOwnerIndex {
12194    /// Package name -> the owner short names its fields name.
12195    owners: HashMap<String, HashSet<String>>,
12196    /// Packages holding at least one field that names no owner.
12197    ownerless_packages: HashSet<String>,
12198}
12199
12200impl CppFieldOwnerIndex {
12201    /// The index of the declarations recorded so far, built when the first
12202    /// question arrives.
12203    fn of<'unit>(
12204        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
12205        file: &ProjectFile,
12206    ) -> Self {
12207        let mut index = Self::default();
12208        for declaration in declarations {
12209            index.record(declaration, file);
12210        }
12211        index
12212    }
12213
12214    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
12215        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
12216            return;
12217        }
12218        let short_name = code_unit.short_name();
12219        let package_name = code_unit.package_name();
12220        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
12221            self.ownerless_packages.insert(package_name.to_string());
12222        }
12223        if !short_name.contains('.') {
12224            return;
12225        }
12226        if !self.owners.contains_key(package_name) {
12227            self.owners
12228                .insert(package_name.to_string(), HashSet::default());
12229        }
12230        let owners = self
12231            .owners
12232            .get_mut(package_name)
12233            .expect("the package entry was just ensured");
12234        for (offset, _) in short_name.match_indices('.') {
12235            let owner = &short_name[..offset];
12236            if !owners.contains(owner) {
12237                owners.insert(owner.to_string());
12238            }
12239        }
12240    }
12241
12242    /// Whether a field declaration in `package_name` names `owner_short_name`
12243    /// as its owner. An empty owner asks about ownerless fields instead.
12244    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
12245        if owner_short_name.is_empty() {
12246            self.ownerless_packages.contains(package_name)
12247        } else {
12248            self.owners
12249                .get(package_name)
12250                .is_some_and(|owners| owners.contains(owner_short_name))
12251        }
12252    }
12253}
12254
12255/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
12256/// debug build asserts every carried answer against and as the release-mode
12257/// parity tests' reference (#2786).
12258#[cfg(any(debug_assertions, test))]
12259fn cpp_declarations_hold_owned_fields<'unit>(
12260    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
12261    file: &ProjectFile,
12262    package_name: &str,
12263    owner_short_name: &str,
12264) -> bool {
12265    let prefix = format!("{owner_short_name}.");
12266    declarations.into_iter().any(|unit| {
12267        unit.kind() == CodeUnitType::Field
12268            && unit.source() == file
12269            && unit.package_name() == package_name
12270            && if owner_short_name.is_empty() {
12271                // Anonymous enum/union parent: its enumerators carry bare
12272                // short names (#2140), so presence means any ownerless
12273                // field in this file.
12274                !unit.short_name().contains(['.', '$'])
12275            } else {
12276                unit.short_name().starts_with(&prefix)
12277            }
12278    })
12279}
12280
12281/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
12282///
12283/// A region reparse is its own tree and is dropped while the walk that made it
12284/// continues, so a later parse can be allocated at the same address and hand out
12285/// the same node ids.  The root's span and shape pin the identity its address
12286/// alone does not: two roots agreeing on all of this are the same parse of the
12287/// same bytes, and a scan of one is a scan of the other.
12288#[derive(PartialEq, Eq, Hash)]
12289pub struct CppTreeIdentity {
12290    root_id: usize,
12291    start_byte: usize,
12292    end_byte: usize,
12293    kind_id: u16,
12294    child_count: usize,
12295}
12296
12297impl CppTreeIdentity {
12298    fn of(root: Node<'_>) -> Self {
12299        Self {
12300            root_id: root.id(),
12301            start_byte: root.start_byte(),
12302            end_byte: root.end_byte(),
12303            kind_id: root.kind_id(),
12304            child_count: root.child_count(),
12305        }
12306    }
12307}
12308
12309/// The namespace forward declarations one tree's prefix holds, folded in as the
12310/// walk asks about them.
12311///
12312/// `scope_for_recovered_exported_class` asks the same question once per
12313/// recovered class, and the answer depends only on the part of the tree that
12314/// starts before the asking node.  Rescanning that prefix per question is
12315/// quadratic in the file, and a generated header whose parse recovery leaves
12316/// thousands of class-like declarations at file scope pays all of it: 1,904
12317/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
12318/// (#2754).  This carries the scan forward instead.  Each question advances the
12319/// traversal from wherever the last one stopped to the asking node's start byte,
12320/// so a whole walk pays at most one pass over the prefix its furthest question
12321/// reaches, and each question then costs a name lookup.
12322#[derive(Default)]
12323pub struct CppNamespaceForwardScan {
12324    /// Every named node starting before this byte has been folded in.
12325    scanned_through: usize,
12326    forwards: HashMap<String, Vec<CppNamespaceForward>>,
12327}
12328
12329impl CppNamespaceForwardScan {
12330    /// Fold in every named node of `root` that starts at or after the fold
12331    /// watermark and before `cutoff`.
12332    ///
12333    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
12334    /// pass owes are exactly the ones no earlier pass reached, and a question
12335    /// about an earlier byte than one already answered costs nothing.
12336    fn advance_to<'tree>(
12337        &mut self,
12338        root: Node<'tree>,
12339        cutoff: usize,
12340        source: &str,
12341        ancestry: &ParentIndex<'tree>,
12342    ) {
12343        if cutoff <= self.scanned_through {
12344            return;
12345        }
12346        let folded_through = self.scanned_through;
12347        let mut cursor = root.walk();
12348        let mut stack = vec![root];
12349        while let Some(current) = stack.pop() {
12350            if (folded_through..cutoff).contains(&current.start_byte())
12351                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12352            {
12353                self.forwards
12354                    .entry(forward.name.clone())
12355                    .or_default()
12356                    .push(forward);
12357            }
12358            // A subtree ending before the watermark holds only nodes an earlier
12359            // pass already folded, and one starting at or after the cutoff is
12360            // outside the prefix being asked about. Skipping both is what keeps
12361            // the total traversal to one pass.
12362            for child in current.named_children(&mut cursor) {
12363                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12364                    stack.push(child);
12365                }
12366            }
12367        }
12368        self.scanned_through = cutoff;
12369    }
12370
12371    /// The one namespace `name` was forward declared in before `recovered_node`.
12372    /// More than one matching forward declaration is ambiguous and answers
12373    /// nothing rather than guessing.
12374    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12375        let mut matching = self
12376            .forwards
12377            .get(name)
12378            .into_iter()
12379            .flatten()
12380            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12381        let first = matching.next()?;
12382        matching
12383            .next()
12384            .is_none()
12385            .then(|| first.package_name.clone())
12386    }
12387}
12388
12389/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
12390/// debug build checks every answer against (and the one the parity tests drive
12391/// directly).  It walks the whole prefix per question, which is exactly the cost
12392/// #2754 removed from the release path.
12393#[cfg(any(debug_assertions, test))]
12394fn unique_earlier_cpp_namespace_forward<'tree>(
12395    recovered_node: Node<'tree>,
12396    name: &str,
12397    source: &str,
12398    ancestry: &ParentIndex<'tree>,
12399) -> Option<String> {
12400    let mut root = recovered_node;
12401    while let Some(parent) = ancestry.parent(root) {
12402        root = parent;
12403    }
12404
12405    let mut candidates = Vec::new();
12406    let mut stack = vec![root];
12407    while let Some(current) = stack.pop() {
12408        if current.start_byte() < recovered_node.start_byte()
12409            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12410            && forward.name == name
12411            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12412        {
12413            candidates.push(forward.package_name);
12414        }
12415
12416        let mut cursor = current.walk();
12417        for child in current.named_children(&mut cursor) {
12418            if child.start_byte() < recovered_node.start_byte() {
12419                stack.push(child);
12420            }
12421        }
12422    }
12423
12424    if candidates.len() == 1 {
12425        candidates.pop()
12426    } else {
12427        None
12428    }
12429}
12430
12431fn malformed_namespace_is_nearest_recovery_region(
12432    namespace_end_byte: usize,
12433    recovered_node: Node<'_>,
12434) -> bool {
12435    let mut root = recovered_node;
12436    while let Some(parent) = root.parent() {
12437        root = parent;
12438    }
12439    let mut cursor = root.walk();
12440    root.named_children(&mut cursor)
12441        .filter(|sibling| {
12442            namespace_end_byte <= sibling.start_byte()
12443                && sibling.end_byte() <= recovered_node.start_byte()
12444        })
12445        .all(is_malformed_namespace_recovery_trivia)
12446}
12447
12448fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12449    matches!(node.kind(), "ERROR" | "comment")
12450        || node.kind().starts_with("preproc_")
12451        || node.kind() == "expression_statement" && node.named_child_count() == 0
12452}
12453
12454/// Return the namespace path for a forward class only when the declaration is
12455/// at namespace scope.  A declaration nested in a function/class body may share
12456/// the same namespace ancestor but cannot identify a top-level class definition.
12457fn cpp_namespace_name_for_forward<'tree>(
12458    node: Node<'tree>,
12459    source: &str,
12460    ancestry: &ParentIndex<'tree>,
12461) -> Option<String> {
12462    cpp_namespace_definition_for_forward(node, ancestry)?;
12463    cpp_lexical_namespace_name(node, source)
12464}
12465
12466fn cpp_namespace_definition_for_forward<'tree>(
12467    node: Node<'tree>,
12468    ancestry: &ParentIndex<'tree>,
12469) -> Option<Node<'tree>> {
12470    let declaration = ancestry.parent(node)?;
12471    let mut ancestor = ancestry.parent(declaration);
12472    while let Some(current) = ancestor {
12473        if matches!(
12474            current.kind(),
12475            "compound_statement"
12476                | "field_declaration_list"
12477                | "class_specifier"
12478                | "struct_specifier"
12479                | "union_specifier"
12480                | "function_definition"
12481                | "lambda_expression"
12482        ) {
12483            return None;
12484        }
12485        if current.kind() == "namespace_definition" {
12486            return Some(current);
12487        }
12488        ancestor = ancestry.parent(current);
12489    }
12490    None
12491}
12492
12493fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12494    match node.kind() {
12495        "template_function" => node
12496            .child_by_field_name("name")
12497            .is_some_and(is_function_pointer_like_inner_declarator),
12498        _ => is_pointer_wrapper_declarator(node),
12499    }
12500}
12501
12502fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12503    match node.kind() {
12504        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12505        "parenthesized_declarator" => node
12506            .child_by_field_name("declarator")
12507            .or_else(|| last_named_child(node))
12508            .is_some_and(is_pointer_wrapper_declarator),
12509        _ => false,
12510    }
12511}
12512
12513fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12514    let cleaned = raw_name.trim_start_matches("template ").trim();
12515    // A leading `::` is the explicit-global marker, not an empty owner segment.
12516    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
12517    // erroneous macro envelope swallowing the first identifier of an
12518    // out-of-line `X::X` constructor, chromium #1573); without this strip the
12519    // split below yields owner_parts `[""]`, constructing a unit with an empty
12520    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
12521    let cleaned = cleaned.trim_start_matches("::");
12522    // Parser recovery can preserve two adjacent scope operators around a
12523    // missing component (for example `X::/**/::method` in compiler diagnostic
12524    // fixtures). Empty components are syntax-recovery artifacts, never C++
12525    // owners. Keeping one as the final owner constructed `short_name=".method"`
12526    // and violated the structured package/short boundary during a large LLVM
12527    // workspace build. This is the same legacy-string-to-FqName bridge as the
12528    // ordinary split above; discard only components that the delimiter itself
12529    // proves empty.
12530    let parts: Vec<_> = cleaned
12531        .split("::")
12532        .filter(|component| !component.is_empty())
12533        .collect();
12534    if parts.is_empty() {
12535        return (None, cleaned.to_string(), scope.package_name.clone());
12536    }
12537    if parts.len() > 1 {
12538        let name = parts.last().unwrap_or(&cleaned).to_string();
12539        let owner_parts = &parts[..parts.len() - 1];
12540        if let Some(class_unit) = &scope.class_unit {
12541            // Lexically inside a class body: the owner is that class, whatever
12542            // the declarator re-qualifies it as.
12543            return (
12544                Some(CppMemberOwner::Unit(class_unit.clone())),
12545                name,
12546                scope.package_name.clone(),
12547            );
12548        }
12549        if !scope.package_name.is_empty() {
12550            // Out-of-line member definition written *inside* an enclosing
12551            // `namespace {}` block (scope package is that namespace). Every
12552            // owner segment before the terminal member is a class-nesting step
12553            // -- an out-of-line nested-class member `Outer::Inner::method` in
12554            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
12555            // namespace path: `using namespace` never brings nested-class
12556            // access into unqualified scope, so C++ always writes the full
12557            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
12558            // that redundantly re-states the enclosing namespace it already
12559            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
12560            // strip that re-qualifying prefix (which duplicates a suffix of the
12561            // enclosing package path) before treating what remains as the
12562            // nested-class chain, so the redundant spelling still lands on the
12563            // same `log4cxx.Foo.method` identity as its header declaration.
12564            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12565            let owner = (!nested.is_empty()).then(|| {
12566                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12567            });
12568            return (owner, name, scope.package_name.clone());
12569        }
12570        // File scope (no enclosing `namespace {}` block, scope package empty).
12571        let (owner, package_name) = if owner_parts.len() > 1 {
12572            // A multi-segment qualifier at file scope with no enclosing
12573            // namespace: treat all but the last owner segment as the namespace
12574            // path and the last as the owning class (`ns1::ns2::Class::method`
12575            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
12576            // is really a namespace or an outer class cannot be told from the
12577            // declarator text alone here, and no enclosing namespace or
12578            // in-index owner is available at per-file extraction to confirm the
12579            // class reading, so the far-more-common namespace interpretation is
12580            // kept rather than guessed away (the nested-class-at-file-scope and
12581            // using-directive-qualified nested-class shapes remain on this
12582            // behavior; see #1121).
12583            (
12584                Some(CppMemberOwner::Chain(vec![
12585                    owner_parts.last().unwrap_or(&"").to_string(),
12586                ])),
12587                owner_parts[..owner_parts.len() - 1].join("::"),
12588            )
12589        } else {
12590            // A bare `Class::member` qualifier at file scope carries no
12591            // namespace segment of its own. The declarator alone cannot say
12592            // which namespace owns `Class` -- but a `using namespace X;`
12593            // directive already in effect at this point in the file (#1093,
12594            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
12595            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
12596            // is the remaining structural signal for it, so fall back to it
12597            // rather than leaving the definition's package empty while its
12598            // header declaration (parsed inside the `namespace {}` block) keeps
12599            // the real one -- an identity split that made the same member
12600            // unresolvable under its own displayed spelling.
12601            (
12602                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12603                cpp_using_directive_namespace_for_bare_owner(scope),
12604            )
12605        };
12606        return (owner, name, package_name);
12607    }
12608
12609    let package_name = scope.package_name.clone();
12610    let owner = scope
12611        .class_unit
12612        .as_ref()
12613        .map(|parent| CppMemberOwner::Unit(parent.clone()));
12614    (owner, cleaned.to_string(), package_name)
12615}
12616
12617/// Drop the leading owner segments of an out-of-line member qualifier that
12618/// merely re-state the enclosing namespace the definition already sits in, so
12619/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
12620/// definition may redundantly write `a::b::Outer::Inner::method` (or the
12621/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
12622/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
12623/// noise, not class-nesting steps. Returns the owner segments with the longest
12624/// such re-qualifying prefix removed (possibly all of them, when the qualifier
12625/// names only the enclosing namespace before the terminal member -- a
12626/// re-qualified free function). `package_name` is the enclosing namespace path
12627/// in its stored `::`-joined form; both sides are split on the same delimiter
12628/// the namespace walker joined them with, so this compares namespace *segments*
12629/// rather than scanning text.
12630fn strip_redundant_namespace_prefix<'a>(
12631    owner_parts: &'a [&'a str],
12632    package_name: &str,
12633) -> &'a [&'a str] {
12634    if package_name.is_empty() {
12635        return owner_parts;
12636    }
12637    let package_segments: Vec<&str> = package_name.split("::").collect();
12638    let max_prefix = owner_parts.len().min(package_segments.len());
12639    for prefix_len in (1..=max_prefix).rev() {
12640        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12641        if &owner_parts[..prefix_len] == package_suffix {
12642            return &owner_parts[prefix_len..];
12643        }
12644    }
12645    owner_parts
12646}
12647
12648/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
12649/// class name at file/namespace scope, from the `using namespace` directives
12650/// visible at this point in the file. Several may be in scope at once (a
12651/// primary `using namespace NS;` alongside deeper conveniences like `using
12652/// namespace NS::helpers;`); since the declarator gives no way to tell which
12653/// one actually declares the owner class, prefer the shallowest (fewest
12654/// `::`-separated segments) as the file's most likely "home" namespace,
12655/// breaking ties by declaration order. Returns an empty string (leaving the
12656/// caller's package unqualified, as before) when no using-namespace directive
12657/// is in scope.
12658fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12659    scope
12660        .visible_using_namespaces
12661        .iter()
12662        .min_by_key(|namespace| namespace.split("::").count())
12663        .cloned()
12664        .unwrap_or_default()
12665}
12666
12667struct CppQualifiedNameComponent {
12668    name: String,
12669    is_template_id: bool,
12670}
12671
12672/// Canonical nested-class chain for an out-of-line class definition written
12673/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
12674/// component per class (`["Outer", "Inner"]`).
12675///
12676/// The enclosing namespace fixes the namespace/class boundary: after an
12677/// optional redundant spelling of that namespace, every component belongs to
12678/// the class chain. File-scope qualified class names remain untouched because
12679/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
12680///
12681/// The components stay structured (rather than being `$`-joined here) so the
12682/// fq construction can push one Type/Nested segment per class; the `$`-joined
12683/// short-name display form is derived at the call sites that need it.
12684fn qualified_class_name_chain(
12685    class_node: Node<'_>,
12686    source: &str,
12687    scope: &ScopeInfo,
12688) -> Option<Vec<String>> {
12689    if scope.package_name.is_empty() || scope.class_unit.is_some() {
12690        return None;
12691    }
12692    let name = class_node.child_by_field_name("name")?;
12693    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12694    if explicitly_global
12695        || components.len() < 2
12696        || components.iter().any(|component| component.is_template_id)
12697    {
12698        return None;
12699    }
12700    let names = components
12701        .iter()
12702        .map(|component| component.name.as_str())
12703        .collect::<Vec<_>>();
12704    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12705    if class_chain.is_empty() {
12706        return None;
12707    }
12708    Some(class_chain.iter().map(|name| name.to_string()).collect())
12709}
12710
12711fn structured_cpp_qualified_components(
12712    qualified_name: Node<'_>,
12713    source: &str,
12714) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12715    if qualified_name.kind() != "qualified_identifier" {
12716        return None;
12717    }
12718
12719    let mut components = Vec::new();
12720    let mut current = qualified_name;
12721    let mut explicitly_global = false;
12722    loop {
12723        if current.kind() == "qualified_identifier" {
12724            if let Some(component) = current.child_by_field_name("scope") {
12725                components.push(canonical_cpp_qualified_component(component, source)?);
12726            } else if components.is_empty() {
12727                explicitly_global = true;
12728            } else {
12729                return None;
12730            }
12731            current = current.child_by_field_name("name")?;
12732        } else {
12733            components.push(canonical_cpp_qualified_component(current, source)?);
12734            break;
12735        }
12736    }
12737    Some((components, explicitly_global))
12738}
12739
12740fn split_structured_templated_cpp_name(
12741    declarator_name: Node<'_>,
12742    source: &str,
12743    scope: &ScopeInfo,
12744) -> Option<(Option<CppMemberOwner>, String, String)> {
12745    let (mut components, explicitly_global) =
12746        structured_cpp_qualified_components(declarator_name, source)?;
12747
12748    let terminal = components.pop()?;
12749    let owner_start = components
12750        .iter()
12751        .position(|component| component.is_template_id)?;
12752    let explicit_package = components[..owner_start]
12753        .iter()
12754        .map(|component| component.name.as_str())
12755        .collect::<Vec<_>>()
12756        .join("::");
12757    let explicit_package_is_empty = explicit_package.is_empty();
12758    let package_name = match (
12759        explicitly_global,
12760        scope.package_name.is_empty(),
12761        explicit_package_is_empty,
12762    ) {
12763        (true, _, _) => explicit_package,
12764        (false, _, true) => scope.package_name.clone(),
12765        (false, true, false) => explicit_package,
12766        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12767    };
12768    // Same identity-split fallback as `split_cpp_name` (#1093): a template
12769    // specialization's owner class named with no namespace segment of its own
12770    // (`explicit_package` empty) at file scope (`explicitly_global` false)
12771    // with nothing enclosing (`package_name` still empty) has no structural
12772    // signal for its namespace besides an in-scope `using namespace X;`.
12773    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12774    {
12775        cpp_using_directive_namespace_for_bare_owner(scope)
12776    } else {
12777        package_name
12778    };
12779    let owner_chain = components[owner_start..]
12780        .iter()
12781        .map(|component| component.name.clone())
12782        .collect::<Vec<_>>();
12783    if owner_chain.is_empty() || terminal.name.is_empty() {
12784        return None;
12785    }
12786
12787    Some((
12788        Some(CppMemberOwner::Chain(owner_chain)),
12789        terminal.name,
12790        package_name,
12791    ))
12792}
12793
12794fn canonical_cpp_qualified_component(
12795    mut component: Node<'_>,
12796    source: &str,
12797) -> Option<CppQualifiedNameComponent> {
12798    let mut is_template_id = false;
12799    loop {
12800        match component.kind() {
12801            "template_type" => {
12802                is_template_id = true;
12803                component = component.child_by_field_name("name")?;
12804            }
12805            "dependent_name" => component = component.named_child(0)?,
12806            "identifier"
12807            | "field_identifier"
12808            | "namespace_identifier"
12809            | "type_identifier"
12810            | "operator_name"
12811            | "destructor_name" => {
12812                let name = normalize_cpp_whitespace(node_text(component, source));
12813                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
12814                    name,
12815                    is_template_id,
12816                });
12817            }
12818            _ => component = component.child_by_field_name("name")?,
12819        }
12820    }
12821}
12822
12823fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
12824    if let Some(name) = macro_decorated_unqualified_name(node) {
12825        return extract_declarator_name(name, source);
12826    }
12827    match node.kind() {
12828        "identifier"
12829        | "field_identifier"
12830        | "type_identifier"
12831        | "operator_name"
12832        | "destructor_name"
12833        | "qualified_identifier" => node_text(node, source).to_string(),
12834        "function_declarator"
12835        | "pointer_declarator"
12836        | "reference_declarator"
12837        | "parenthesized_declarator"
12838        | "array_declarator"
12839        | "template_function" => node
12840            .child_by_field_name("declarator")
12841            .or_else(|| node.child_by_field_name("name"))
12842            .or_else(|| last_named_child(node))
12843            .map(|child| extract_declarator_name(child, source))
12844            .unwrap_or_else(|| node_text(node, source).to_string()),
12845        _ => node
12846            .child_by_field_name("name")
12847            .map(|child| extract_declarator_name(child, source))
12848            .unwrap_or_else(|| node_text(node, source).to_string()),
12849    }
12850}
12851
12852/// Extract a callable identity only through declaration-shaped AST nodes.
12853/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
12854/// expose the call's parameter list as a false function declarator; accepting
12855/// arbitrary node text there emitted bogus names such as `.*f`.
12856fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
12857    if let Some(name) = macro_decorated_unqualified_name(node) {
12858        return extract_callable_declarator_name(name, source);
12859    }
12860    match node.kind() {
12861        "identifier"
12862        | "field_identifier"
12863        | "type_identifier"
12864        | "operator_name"
12865        | "destructor_name"
12866        | "qualified_identifier" => Some(node_text(node, source).to_string()),
12867        "function_declarator"
12868        | "pointer_declarator"
12869        | "reference_declarator"
12870        | "parenthesized_declarator"
12871        | "array_declarator"
12872        | "template_function" => node
12873            .child_by_field_name("declarator")
12874            .or_else(|| node.child_by_field_name("name"))
12875            .and_then(|child| extract_callable_declarator_name(child, source)),
12876        _ => None,
12877    }
12878}
12879
12880fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
12881    loop {
12882        if let Some(name) = macro_decorated_unqualified_name(node) {
12883            node = name;
12884            continue;
12885        }
12886        match node.kind() {
12887            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
12888                let name = node_text(node, source).trim().to_string();
12889                return (!name.is_empty()).then_some(name);
12890            }
12891            _ => {
12892                node = node
12893                    .child_by_field_name("declarator")
12894                    .or_else(|| node.child_by_field_name("name"))
12895                    .or_else(|| last_named_child(node))?;
12896            }
12897        }
12898    }
12899}
12900
12901/// Recover a C field whose name is wrapped in the function-like
12902/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
12903/// function declarator, while retaining its argument as a structured child.
12904/// The exact macro name and one-argument shape keep ordinary function-pointer
12905/// fields and unknown malformed declarators fail-closed.
12906#[derive(Clone, Copy)]
12907pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
12908    pub(crate) name: Node<'tree>,
12909    pub(crate) declarator: Node<'tree>,
12910}
12911
12912impl RecoveredFunctionLikeFieldDeclarator<'_> {
12913    pub(crate) fn pointer_depth(self) -> i32 {
12914        let mut depth = 0;
12915        let mut current = self.declarator;
12916        while current.kind() != "function_declarator" {
12917            if current.kind() == "pointer_declarator" {
12918                depth += 1;
12919            }
12920            current = current
12921                .child_by_field_name("declarator")
12922                .expect("recovered field wrapper has an inner declarator");
12923        }
12924        depth
12925    }
12926}
12927
12928/// Whether a class-scope field declaration is really a function-like macro
12929/// invocation whose replacement the parser never saw.
12930///
12931/// The invocation carries no terminator of its own, so tree-sitter invents a
12932/// zero-width `;` and reads the macro name as the member's type and the
12933/// argument list as a `parenthesized_declarator`. That shape is not a member:
12934/// a member terminates itself, and with a real terminator the grammar reads
12935/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
12936/// type can name the macro being invoked; a builtin type keeps whatever
12937/// recovery the ordinary declarator walk makes of it.
12938fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
12939    if node.kind() != "field_declaration" {
12940        return false;
12941    }
12942    let Some(type_node) = node.child_by_field_name("type") else {
12943        return false;
12944    };
12945    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
12946        return false;
12947    }
12948    let Some(declarator) = node.child_by_field_name("declarator") else {
12949        return false;
12950    };
12951    if declarator.kind() != "parenthesized_declarator" {
12952        return false;
12953    }
12954    let mut invented_terminator = false;
12955    for child in children_iter(node) {
12956        if child.kind() != ";" {
12957            continue;
12958        }
12959        if !child.is_missing() {
12960            return false;
12961        }
12962        invented_terminator = true;
12963    }
12964    invented_terminator
12965}
12966
12967/// An access label that a class-scope function-like macro invocation
12968/// swallowed, as [`cpp_macro_swallowed_access_label`] reads it.
12969pub(crate) struct CppSwallowedAccessLabel<'tree> {
12970    /// The access keyword, which the grammar read as a `field_identifier`.
12971    pub(crate) keyword: Node<'tree>,
12972    /// `"public"`, `"protected"` or `"private"`.
12973    pub(crate) access: &'static str,
12974}
12975
12976/// Whether a class-body member is a function-like macro invocation that
12977/// swallowed the access label written after it, and with it the members under
12978/// that label.
12979///
12980/// `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)` carries no terminator of its own
12981/// (#3298). The macro-declaration grammar (brokk-tree-sitter-cpp 0.24.4) reads
12982/// the invocation as a `macro_type_specifier`, the `public` after it as the
12983/// member's `field_identifier` declarator, and the label's `:` as a bitfield
12984/// whose width expression shreds the next member, so the constructor written
12985/// under the label is left with no declarator at all. An access keyword is
12986/// never a declarator name, and a bitfield after it is how the parser spent
12987/// the label's colon, so the shape is the evidence. The access ladders read
12988/// the label from it too, because it still applies to every member after it.
12989pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
12990    node: Node<'tree>,
12991    source: &str,
12992) -> Option<CppSwallowedAccessLabel<'tree>> {
12993    if !matches!(node.kind(), "field_declaration" | "function_definition") {
12994        return None;
12995    }
12996    node.child_by_field_name("type")
12997        .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
12998    let keyword = node
12999        .child_by_field_name("declarator")
13000        .filter(|declarator| declarator.kind() == "field_identifier")?;
13001    let access = match node_text(keyword, source) {
13002        "public" => "public",
13003        "protected" => "protected",
13004        "private" => "private",
13005        _ => return None,
13006    };
13007    let mut clause = keyword.next_sibling()?;
13008    if clause.kind() == "ERROR" {
13009        clause = clause.child(0)?;
13010    }
13011    if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13012        return None;
13013    }
13014    Some(CppSwallowedAccessLabel { keyword, access })
13015}
13016
13017pub(crate) fn recovered_function_like_field_declarator<'tree>(
13018    node: Node<'tree>,
13019    source: &str,
13020) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13021    if node.kind() != "field_declaration" {
13022        return None;
13023    }
13024    let outer_declarator = node.child_by_field_name("declarator")?;
13025    let mut declarator = outer_declarator;
13026    while matches!(
13027        declarator.kind(),
13028        "pointer_declarator"
13029            | "reference_declarator"
13030            | "array_declarator"
13031            | "parenthesized_declarator"
13032    ) {
13033        declarator = declarator.child_by_field_name("declarator")?;
13034    }
13035    if declarator.kind() != "function_declarator" {
13036        return None;
13037    }
13038    let macro_name = declarator.child_by_field_name("declarator")?;
13039    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13040    {
13041        return None;
13042    }
13043    let parameters = declarator.child_by_field_name("parameters")?;
13044    let mut cursor = parameters.walk();
13045    let mut arguments = parameters.named_children(&mut cursor);
13046    let parameter = arguments.next()?;
13047    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13048        return None;
13049    }
13050    let name = parameter.child_by_field_name("type").filter(|argument| {
13051        matches!(
13052            argument.kind(),
13053            "identifier" | "field_identifier" | "type_identifier"
13054        )
13055    })?;
13056    Some(RecoveredFunctionLikeFieldDeclarator {
13057        name,
13058        declarator: outer_declarator,
13059    })
13060}
13061
13062use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13063
13064fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13065    let name_node = node.child_by_field_name("name")?;
13066    let name = normalize_cpp_whitespace(node_text(name_node, source));
13067    (!name.is_empty()).then_some(name)
13068}
13069
13070fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13071    if node.kind() != "declaration" {
13072        return Vec::new();
13073    }
13074    // The `typedef`/`using` keyword: a `type_identifier` in the type field,
13075    // or -- under the macro-declaration grammar, which reads the keyword as
13076    // an object-like macro -- the identifier a leading `macro_modifier`
13077    // wraps, with the aliased type in the type field.
13078    let keyword_is_alias = node
13079        .child_by_field_name("type")
13080        .filter(|node| {
13081            node.kind() == "type_identifier"
13082                && matches!(node_text(*node, source), "using" | "typedef")
13083        })
13084        .map(|keyword| node_text(keyword, source).to_owned())
13085        .or_else(|| {
13086            node.named_children(&mut node.walk())
13087                .find_map(macro_modifier_identifier)
13088                .map(|identifier| node_text(identifier, source).to_owned())
13089                .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13090        });
13091    let Some(keyword_is_alias) = keyword_is_alias else {
13092        return Vec::new();
13093    };
13094    let Some(declarator) = node.child_by_field_name("declarator") else {
13095        return Vec::new();
13096    };
13097    if keyword_is_alias == "using"
13098        && (declarator.kind() != "init_declarator"
13099            || declarator.child_by_field_name("value").is_none())
13100    {
13101        return Vec::new();
13102    }
13103    if keyword_is_alias == "typedef"
13104        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13105    {
13106        return vec![alias_name];
13107    }
13108    extract_typedef_declarator_name(declarator, source)
13109        .into_iter()
13110        .collect()
13111}
13112
13113/// The RHS retained by the same malformed alias shapes declaration admission
13114/// accepts. The alias name proves which declarator owns this source evidence.
13115pub(crate) fn recovered_alias_type_node<'tree>(
13116    node: Node<'tree>,
13117    alias_name: &str,
13118    source: &str,
13119) -> Option<Node<'tree>> {
13120    if node.kind() != "declaration" {
13121        return None;
13122    }
13123    let declarator = node.child_by_field_name("declarator")?;
13124    let admitted = recovered_type_alias_names(node, source)
13125        .iter()
13126        .any(|name| name == alias_name)
13127        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13128    if !admitted {
13129        return None;
13130    }
13131    if declarator.kind() == "init_declarator" {
13132        return declarator.child_by_field_name("value");
13133    }
13134    // The macro-declaration grammar keeps the aliased type in the type field
13135    // and the keyword as a `macro_modifier`; the alias name is the
13136    // declarator, so the type field is the RHS evidence.
13137    if node
13138        .named_children(&mut node.walk())
13139        .find_map(macro_modifier_identifier)
13140        .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13141    {
13142        return node.child_by_field_name("type");
13143    }
13144    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13145        .then_some(declarator)
13146}
13147
13148fn recovered_typedef_error_alias_name(
13149    declaration: Node<'_>,
13150    declarator: Node<'_>,
13151    source: &str,
13152) -> Option<String> {
13153    // An export macro between `class` and its name can make tree-sitter parse
13154    // the recovered class body as a function body. In that shape,
13155    //
13156    //     typedef spi::Filter BASE_CLASS;
13157    //
13158    // becomes a declaration whose `declarator` is the underlying qualified
13159    // type (`spi::Filter`) and whose actual alias name is displaced into the
13160    // following ERROR node. Do not publish the terminal underlying type
13161    // (`Filter`) as a false class-owned alias.
13162    if declarator.kind() != "qualified_identifier" {
13163        return None;
13164    }
13165    let mut cursor = declaration.walk();
13166    let mut errors = declaration
13167        .named_children(&mut cursor)
13168        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13169    let error = errors.next()?;
13170    if errors.next().is_some() || error.named_child_count() != 1 {
13171        return None;
13172    }
13173    let name = error.named_child(0)?;
13174    if !matches!(
13175        name.kind(),
13176        "identifier" | "field_identifier" | "type_identifier"
13177    ) {
13178        return None;
13179    }
13180    let name = normalize_cpp_whitespace(node_text(name, source));
13181    (!name.is_empty()).then_some(name)
13182}
13183
13184fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13185    // A function-like token in the type position can make tree-sitter expose
13186    // its argument as a parenthesized declarator. Do not publish that argument
13187    // as an alias. The macro-specific recovery below handles the proven shape.
13188    if fragmented_parenthesized_typedef_type(node).is_some() {
13189        return Vec::new();
13190    }
13191    let has_function_like_macro_type = node
13192        .child_by_field_name("type")
13193        .filter(|type_node| type_node.kind() == "type_identifier")
13194        .is_some_and(|type_node| {
13195            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13196        });
13197    let mut names = Vec::new();
13198    let mut cursor = node.walk();
13199    for declarator in node.children_by_field_name("declarator", &mut cursor) {
13200        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13201            continue;
13202        }
13203        if let Some(name) = extract_typedef_declarator_name(declarator, source)
13204            && !names.contains(&name)
13205        {
13206            names.push(name);
13207        }
13208    }
13209    names
13210}
13211
13212struct RecoveredMacroTypedefAlias<'tree> {
13213    name: String,
13214    end_node: Node<'tree>,
13215}
13216
13217/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
13218/// into an identifier expression statement. The uppercase macro token, missing
13219/// typedef terminator, and complete sibling terminator prove this exact shape.
13220fn recovered_macro_typedef_alias<'tree>(
13221    node: Node<'tree>,
13222    source: &str,
13223) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13224    let type_node = fragmented_parenthesized_typedef_type(node)?;
13225    if type_node.kind() != "type_identifier"
13226        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13227    {
13228        return None;
13229    }
13230
13231    let end_node = node.next_named_sibling()?;
13232    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13233        return None;
13234    }
13235    let name_node = end_node.named_child(0)?;
13236    if name_node.kind() != "identifier" {
13237        return None;
13238    }
13239    let has_terminator = (0..end_node.child_count()).any(|index| {
13240        end_node
13241            .child(index)
13242            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13243    });
13244    if !has_terminator {
13245        return None;
13246    }
13247    let name = normalize_cpp_whitespace(node_text(name_node, source));
13248    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13249}
13250
13251fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13252    if node.kind() != "type_definition" {
13253        return None;
13254    }
13255    let mut declarator_cursor = node.walk();
13256    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13257    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13258        return None;
13259    }
13260    let has_missing_terminator = (0..node.child_count()).any(|index| {
13261        node.child(index)
13262            .is_some_and(|child| child.kind() == ";" && child.is_missing())
13263    });
13264    if !has_missing_terminator {
13265        return None;
13266    }
13267    node.child_by_field_name("type")
13268}
13269
13270pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13271    match node.kind() {
13272        "identifier" | "field_identifier" | "type_identifier" => {
13273            direct_identifier_name(node, source)
13274        }
13275        "qualified_identifier" => node
13276            .child_by_field_name("name")
13277            .and_then(|name| extract_typedef_declarator_name(name, source)),
13278        _ => node
13279            .child_by_field_name("declarator")
13280            .or_else(|| node.child_by_field_name("name"))
13281            .or_else(|| last_named_child(node))
13282            .and_then(|child| extract_typedef_declarator_name(child, source)),
13283    }
13284}
13285
13286fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13287    if let Some(name_node) = node.child_by_field_name("name") {
13288        let name = normalize_cpp_whitespace(node_text(name_node, source));
13289        return (!name.is_empty()).then_some(name);
13290    }
13291    let mut cursor = node.walk();
13292    node.named_children(&mut cursor)
13293        .find(|child| {
13294            matches!(
13295                child.kind(),
13296                "identifier" | "field_identifier" | "type_identifier"
13297            )
13298        })
13299        .and_then(|name_node| direct_identifier_name(name_node, source))
13300}
13301
13302fn render_cpp_type_signature(
13303    node: Node<'_>,
13304    source: &str,
13305    template_signature: Option<&str>,
13306) -> String {
13307    let text = normalize_cpp_whitespace(node_text(node, source));
13308    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13309    let rendered = if head.ends_with(';') {
13310        head.to_string()
13311    } else {
13312        format!("{head} {{")
13313    };
13314    if let Some(template_signature) = template_signature {
13315        format!("template {template_signature} {rendered}")
13316    } else {
13317        rendered
13318    }
13319}
13320
13321fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13322    if let Some(recovered) = recovered_pyobject_head_field(node, source)
13323        && recovered.declarator == declarator
13324    {
13325        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13326        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13327        return format!("{type_text} {declarator};");
13328    }
13329    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13330        && recovered.name == declarator
13331    {
13332        let type_text = node
13333            .child_by_field_name("type")
13334            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13335            .unwrap_or_default();
13336        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13337        let mut prefix = String::new();
13338        let mut suffix = String::new();
13339        let mut current = recovered.declarator;
13340        while current.kind() != "function_declarator" {
13341            match current.kind() {
13342                "pointer_declarator" => prefix.push('*'),
13343                "reference_declarator" => prefix.push('&'),
13344                "array_declarator" => {
13345                    let size = current
13346                        .child_by_field_name("size")
13347                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13348                        .unwrap_or_default();
13349                    suffix.push('[');
13350                    suffix.push_str(&size);
13351                    suffix.push(']');
13352                }
13353                "parenthesized_declarator" => {}
13354                _ => unreachable!("validated recovered field declarator wrapper"),
13355            }
13356            current = current
13357                .child_by_field_name("declarator")
13358                .expect("recovered field wrapper has an inner declarator");
13359        }
13360        let separator = if prefix.is_empty() { "" } else { " " };
13361        return format!("{type_text} {prefix}{separator}{name}{suffix};");
13362    }
13363    if let Some(signature) =
13364        render_recovered_macro_qualified_field_signature(node, declarator, source)
13365    {
13366        return signature;
13367    }
13368    let prefix = cpp_declaration_prefix(node, source);
13369    let name = extract_variable_name(declarator, source).unwrap_or_default();
13370    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13371    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13372        || (prefix.ends_with('&') && raw_suffix == "&")
13373    {
13374        String::new()
13375    } else {
13376        raw_suffix
13377    };
13378
13379    let mut rendered = if suffix.is_empty() {
13380        format!("{prefix} {name}")
13381    } else if suffix.starts_with('*') || suffix.starts_with('&') {
13382        format!("{prefix}{suffix} {name}")
13383    } else if suffix.starts_with('[') || suffix.starts_with('(') {
13384        format!("{prefix} {name}{suffix}")
13385    } else {
13386        format!("{prefix} {suffix}{name}")
13387    };
13388    rendered = collapse_cpp_whitespace(&rendered);
13389
13390    // Check the terminal token directly instead of normalizing the whole
13391    // declaration, which can copy a large initializer for each field.
13392    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13393        format!("{rendered} = {initializer};")
13394    } else if cpp_direct_semicolon(node).is_some() {
13395        format!("{rendered};")
13396    } else {
13397        rendered
13398    }
13399}
13400
13401fn render_recovered_macro_qualified_field_signature(
13402    node: Node<'_>,
13403    declarator: Node<'_>,
13404    source: &str,
13405) -> Option<String> {
13406    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13407    if !recovered
13408        .iter()
13409        .any(|candidate| same_node(*candidate, declarator))
13410    {
13411        return None;
13412    }
13413    let pseudo_declarator = node.child_by_field_name("declarator")?;
13414    let mut cursor = node.walk();
13415    let clause = node
13416        .named_children(&mut cursor)
13417        .find(|child| child.kind() == "bitfield_clause")?;
13418    let mut cursor = clause.walk();
13419    let error = clause
13420        .named_children(&mut cursor)
13421        .find(|child| child.kind() == "ERROR")?;
13422    let qualified_type =
13423        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13424    let prefix = cpp_declaration_prefix(node, source);
13425    let name = extract_variable_name(declarator, source)?;
13426    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13427    let mut rendered = if suffix.is_empty() {
13428        format!("{prefix} {qualified_type} {name}")
13429    } else {
13430        format!("{prefix} {qualified_type} {suffix} {name}")
13431    };
13432    rendered = collapse_cpp_whitespace(&rendered);
13433
13434    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13435        Some(format!(
13436            "{rendered} = {};",
13437            normalize_cpp_whitespace(node_text(initializer, source))
13438        ))
13439    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13440        Some(format!("{rendered} = {initializer};"))
13441    } else {
13442        Some(format!("{rendered};"))
13443    }
13444}
13445
13446fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13447    match node.kind() {
13448        "pointer_expression" => {
13449            let operator = node
13450                .child_by_field_name("operator")
13451                .or_else(|| node.child(0))
13452                .map(|operator| node_text(operator, source))
13453                .unwrap_or("*");
13454            let argument = node
13455                .child_by_field_name("argument")
13456                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13457                .unwrap_or_default();
13458            format!("{operator}{argument}")
13459        }
13460        "unary_expression" => {
13461            let operator = node
13462                .child_by_field_name("operator")
13463                .or_else(|| node.child(0))
13464                .map(|operator| node_text(operator, source))
13465                .unwrap_or_default();
13466            let argument = node
13467                .child_by_field_name("argument")
13468                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13469                .unwrap_or_default();
13470            format!("{operator}{argument}")
13471        }
13472        "identifier" | "field_identifier" => String::new(),
13473        _ => cpp_declarator_suffix_without_name(node, source),
13474    }
13475}
13476
13477fn recovered_macro_qualified_field_initializer<'tree>(
13478    clause: Node<'tree>,
13479    declarator: Node<'tree>,
13480) -> Option<Node<'tree>> {
13481    let mut stack = vec![clause];
13482    while let Some(current) = stack.pop() {
13483        if current.kind() == "assignment_expression"
13484            && current
13485                .child_by_field_name("left")
13486                .is_some_and(|left| same_node(left, declarator))
13487        {
13488            return current.child_by_field_name("right");
13489        }
13490        let mut cursor = current.walk();
13491        stack.extend(current.named_children(&mut cursor));
13492    }
13493    None
13494}
13495
13496fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13497    let text = node_text(node, source);
13498    let mut cursor = node.walk();
13499    let first_declarator = node.named_children(&mut cursor).find(|child| {
13500        matches!(
13501            child.kind(),
13502            "init_declarator"
13503                | "identifier"
13504                | "field_identifier"
13505                | "pointer_declarator"
13506                | "reference_declarator"
13507                | "array_declarator"
13508                | "function_declarator"
13509        )
13510    });
13511    let prefix = if let Some(first_declarator) = first_declarator {
13512        let end = first_declarator
13513            .start_byte()
13514            .saturating_sub(node.start_byte());
13515        let mut prefix = text.get(..end).unwrap_or(text).to_string();
13516        let declarator_suffix = match first_declarator.kind() {
13517            "init_declarator" => first_declarator
13518                .child_by_field_name("declarator")
13519                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13520                .unwrap_or_default(),
13521            _ => cpp_declarator_suffix_without_name(first_declarator, source),
13522        };
13523        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13524            prefix.push_str(&declarator_suffix);
13525        }
13526        return collapse_cpp_whitespace(&prefix)
13527            .trim_end_matches(',')
13528            .trim_end_matches(';')
13529            .trim()
13530            .to_string();
13531    } else {
13532        text
13533    };
13534    collapse_cpp_whitespace(prefix)
13535        .trim_end_matches(',')
13536        .trim_end_matches(';')
13537        .trim()
13538        .to_string()
13539}
13540
13541fn cpp_preserved_initializer(
13542    declaration_node: Node<'_>,
13543    declarator: Node<'_>,
13544    source: &str,
13545) -> Option<String> {
13546    let name = extract_variable_name(declarator, source)?;
13547    let mut cursor = declaration_node.walk();
13548    for child in declaration_node.named_children(&mut cursor) {
13549        if child.kind() != "init_declarator" {
13550            continue;
13551        }
13552        let Some(inner) = child.child_by_field_name("declarator") else {
13553            continue;
13554        };
13555        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13556            continue;
13557        }
13558        let value = child.child_by_field_name("value")?;
13559        let kind = value.kind();
13560        if matches!(
13561            kind,
13562            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13563        ) {
13564            return Some(normalize_cpp_whitespace(node_text(value, source)));
13565        }
13566        break;
13567    }
13568    // The current grammar keeps a member initializer as a `default_value`
13569    // field beside the declarator rather than inside an `init_declarator`.
13570    if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13571        && let Some(value) = equals.next_sibling().filter(|node| {
13572            declaration_node
13573                .child_by_field_name("default_value")
13574                .is_some_and(|default| same_node(default, *node))
13575        })
13576    {
13577        let kind = value.kind();
13578        if matches!(
13579            kind,
13580            "number_literal"
13581                | "float_literal"
13582                | "char_literal"
13583                | "true"
13584                | "false"
13585                | "null"
13586                | "nullptr"
13587                | "identifier"
13588        ) {
13589            return Some(normalize_cpp_whitespace(node_text(value, source)));
13590        }
13591    }
13592    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13593    let pattern = format!(
13594        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13595        regex::escape(&name)
13596    );
13597    Regex::new(&pattern)
13598        .ok()
13599        .and_then(|regex| regex.captures(&declaration_text))
13600        .and_then(|captures| captures.get(1))
13601        .map(|value| value.as_str().to_string())
13602}
13603
13604fn render_cpp_function_display_signature_from_node<'tree>(
13605    node: Node<'tree>,
13606    source: &str,
13607    template_signature: Option<&str>,
13608    has_body: bool,
13609    ancestry: &ParentIndex<'tree>,
13610) -> String {
13611    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13612    let parent_text = node_text(root, source);
13613    let body_local_start = root
13614        .child_by_field_name("body")
13615        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13616        .unwrap_or(parent_text.len());
13617    let display = parent_text
13618        .get(..body_local_start)
13619        .unwrap_or(parent_text)
13620        .trim()
13621        .trim();
13622    let display = if let Some(template_signature) = template_signature {
13623        if display.starts_with("template ") {
13624            display.to_string()
13625        } else {
13626            format!("template {template_signature} {display}")
13627        }
13628    } else {
13629        display.to_string()
13630    };
13631    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13632    if has_body {
13633        format!("{display} {{...}}")
13634    } else {
13635        format!("{display};")
13636    }
13637}
13638
13639fn cpp_template_signature(
13640    template_node: Node<'_>,
13641    declaration_child: Node<'_>,
13642    source: &str,
13643) -> Option<String> {
13644    let text = source
13645        .get(template_node.start_byte()..declaration_child.start_byte())
13646        .unwrap_or("");
13647    let text = normalize_cpp_whitespace(text);
13648    let start = text.find('<')?;
13649    let end = text.rfind('>')?;
13650    if end < start {
13651        return None;
13652    }
13653    Some(text[start..=end].to_string())
13654}
13655
13656struct RecoveredFragmentedPartialSpecialization<'tree> {
13657    declaration_node: Node<'tree>,
13658    name: String,
13659    range: Range,
13660    prefix_members: Vec<Node<'tree>>,
13661    member_siblings: Vec<Node<'tree>>,
13662    following_declarations: Vec<Node<'tree>>,
13663}
13664
13665struct RecoveredFragmentedPreprocessorClass<'tree> {
13666    declaration_node: Node<'tree>,
13667    class_node: Node<'tree>,
13668    body: Node<'tree>,
13669    name: String,
13670    range: Range,
13671    tail_members: Vec<Node<'tree>>,
13672    member_siblings: Vec<Node<'tree>>,
13673}
13674
13675/// Recover a class whose preprocessor-fragmented parse closes at an early
13676/// member body and publishes the remaining in-class declarations as siblings
13677/// of the surrounding alternative. Primary classes are admitted only when an
13678/// earlier branch contains the matching bodyless declaration and the class
13679/// node retains the displaced `#endif`. Partial specializations instead carry
13680/// their identity structurally in the `template_type` name and template
13681/// metadata. Retain the original AST nodes and re-own only the siblings through
13682/// the displaced structural `};` terminator.
13683fn recover_fragmented_preprocessor_class<'tree>(
13684    template_node: Node<'tree>,
13685    source: &str,
13686    ancestry: &ParentIndex<'tree>,
13687) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13688    let alternative = ancestry.parent(template_node)?;
13689    if alternative.kind() != "preproc_else" {
13690        return None;
13691    }
13692    let conditional = alternative.parent()?;
13693    if conditional.kind() != "preproc_if" {
13694        return None;
13695    }
13696    let declaration_node = template_node
13697        .named_children(&mut template_node.walk())
13698        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13699    let class_node = declaration_node
13700        .named_children(&mut declaration_node.walk())
13701        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13702    let body = cpp_body_node(class_node)?;
13703    if class_node.end_byte() >= declaration_node.end_byte() {
13704        return None;
13705    }
13706    let name = class_like_name(class_node, source, ancestry)?;
13707    let is_partial_specialization = class_node
13708        .child_by_field_name("name")
13709        .is_some_and(|class_name| class_name.kind() == "template_type");
13710    if is_partial_specialization {
13711        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13712        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13713            return None;
13714        }
13715    } else {
13716        if !class_has_displaced_preprocessor_terminator(class_node) {
13717            return None;
13718        }
13719        let matching_other_branch = conditional
13720            .named_children(&mut conditional.walk())
13721            .take_while(|child| !same_node(*child, alternative))
13722            .filter(|child| child.kind() == "template_declaration")
13723            .filter_map(first_class_like_child)
13724            .any(|candidate| {
13725                cpp_body_node(candidate).is_none()
13726                    && class_like_name(candidate, source, ancestry).as_deref()
13727                        == Some(name.as_str())
13728            });
13729        if !matching_other_branch {
13730            return None;
13731        }
13732    }
13733
13734    let mut tail_members = Vec::new();
13735    let mut saw_class = false;
13736    let mut declaration_cursor = declaration_node.walk();
13737    for child in declaration_node.named_children(&mut declaration_cursor) {
13738        if same_node(child, class_node) {
13739            saw_class = true;
13740        } else if saw_class {
13741            tail_members.push(child);
13742        }
13743    }
13744
13745    let mut member_siblings = Vec::new();
13746    let mut saw_template = false;
13747    let mut terminator = None;
13748    for index in 0..alternative.child_count() {
13749        let Some(child) = alternative.child(index) else {
13750            continue;
13751        };
13752        if same_node(child, template_node) {
13753            saw_template = true;
13754            continue;
13755        }
13756        if !saw_template {
13757            continue;
13758        }
13759        if displaced_fragmented_class_terminator(alternative, index) {
13760            terminator = alternative.child(index + 1);
13761            break;
13762        }
13763        if child.is_named() {
13764            member_siblings.push(child);
13765        }
13766    }
13767    let terminator = terminator?;
13768    Some(RecoveredFragmentedPreprocessorClass {
13769        declaration_node,
13770        class_node,
13771        body,
13772        name,
13773        range: Range {
13774            start_byte: class_node.start_byte(),
13775            end_byte: terminator.end_byte(),
13776            start_line: class_node.start_position().row + 1,
13777            end_line: terminator.end_position().row + 1,
13778        },
13779        tail_members,
13780        member_siblings,
13781    })
13782}
13783
13784fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
13785    (0..class_node.child_count()).any(|index| {
13786        class_node.child(index).is_some_and(|child| {
13787            child.kind() == "ERROR"
13788                && (0..child.child_count()).any(|error_index| {
13789                    child
13790                        .child(error_index)
13791                        .is_some_and(|token| token.kind() == "#endif")
13792                })
13793        })
13794    })
13795}
13796
13797/// The real `#endif` that tree-sitter consumed inside an error subtree.
13798///
13799/// A preprocessor directive inside a malformed array bound can cause later
13800/// declarations to remain children of the conditional. The non-missing token
13801/// still gives the exact structured boundary. Ignore nested conditionals and
13802/// select the last unpaired error-owned token. Tree-sitter can pair a later
13803/// outer `#endif` with this conditional, so the direct terminator is not
13804/// necessarily missing.
13805pub fn cpp_displaced_preprocessor_terminator<'tree>(
13806    conditional: Node<'tree>,
13807) -> Option<Node<'tree>> {
13808    if !conditional.has_error() {
13809        return None;
13810    }
13811    let has_concrete_direct_terminator = conditional
13812        .child_count()
13813        .checked_sub(1)
13814        .and_then(|index| conditional.child(index))
13815        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
13816    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
13817        // A structured alternative proves that the direct `#endif` closes
13818        // this family. An error-owned terminator inside either branch belongs
13819        // to a damaged nested conditional, not to this one.
13820        return None;
13821    }
13822    // Pair concrete directive tokens while walking the recovered subtree. An
13823    // inner conditional may be represented only by its `#ifndef`/`#endif`
13824    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
13825    // sufficient: an inner `#endif` can otherwise be mistaken for this
13826    // conditional's displaced terminator.
13827    let mut displaced = None;
13828    let mut conditional_depth = 0usize;
13829    let mut stack = children_iter(conditional)
13830        .map(|child| (child, false))
13831        .collect::<Vec<_>>();
13832    stack.reverse();
13833    while let Some((node, inside_error)) = stack.pop() {
13834        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13835            continue;
13836        }
13837        if node != conditional
13838            && matches!(
13839                node.kind(),
13840                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
13841            )
13842        {
13843            continue;
13844        }
13845        let inside_error = inside_error || node.kind() == "ERROR";
13846        match node.kind() {
13847            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
13848                conditional_depth += 1;
13849            }
13850            "#endif" if !node.is_missing() => {
13851                if conditional_depth == 0
13852                    && inside_error
13853                    && displaced
13854                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
13855                {
13856                    displaced = Some(node);
13857                }
13858                conditional_depth = conditional_depth.saturating_sub(1);
13859            }
13860            _ => {}
13861        }
13862        let first_pushed = stack.len();
13863        stack.extend(children_iter(node).map(|child| (child, inside_error)));
13864        stack[first_pushed..].reverse();
13865    }
13866    displaced
13867}
13868
13869/// The first `#else`/`#elif` of `conditional` that tree-sitter displaced into
13870/// an `ERROR` inside the conditional's own branch.
13871///
13872/// The macro-declaration grammar can read an unterminated end sentinel, the
13873/// branch directive and the next branch's begin sentinel as one declaration
13874/// (`END_NS #else BEGIN_NS class C {};`, #6325): the `#else` becomes an
13875/// `ERROR` child and the conditional loses its `alternative`. The token is
13876/// still the structured branch boundary. Nested conditionals own their own
13877/// directives, including ones represented only by error-owned `#if`/`#endif`
13878/// tokens, so the walk pairs those the same way
13879/// [`cpp_displaced_preprocessor_terminator`] does.
13880pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
13881    if !conditional.has_error() {
13882        return None;
13883    }
13884    let alternative = conditional.child_by_field_name("alternative");
13885    let mut conditional_depth = 0usize;
13886    let mut stack = children_iter(conditional)
13887        .filter(|child| Some(*child) != alternative)
13888        .map(|child| (child, false))
13889        .collect::<Vec<_>>();
13890    stack.reverse();
13891    while let Some((node, inside_error)) = stack.pop() {
13892        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
13893            continue;
13894        }
13895        if matches!(
13896            node.kind(),
13897            "preproc_if"
13898                | "preproc_ifdef"
13899                | "preproc_ifndef"
13900                | "preproc_elif"
13901                | "preproc_elifdef"
13902                | "preproc_else"
13903        ) {
13904            continue;
13905        }
13906        let inside_error = inside_error || node.kind() == "ERROR";
13907        match node.kind() {
13908            "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
13909            "#endif" if !node.is_missing() => {
13910                conditional_depth = conditional_depth.saturating_sub(1);
13911            }
13912            "#else" | "#elif" | "#elifdef" | "#elifndef"
13913                if inside_error && conditional_depth == 0 && !node.is_missing() =>
13914            {
13915                return Some(node);
13916            }
13917            _ => {}
13918        }
13919        let first_pushed = stack.len();
13920        stack.extend(children_iter(node).map(|child| (child, inside_error)));
13921        stack[first_pushed..].reverse();
13922    }
13923    None
13924}
13925
13926/// The effective end of a conditional whose real terminator tree-sitter
13927/// displaced into declaration recovery.
13928///
13929/// Most damaged conditionals retain a concrete `#endif` token below an
13930/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
13931/// boundary. A preprocessor family that selects the middle of a declaration
13932/// can lose the directive tokens entirely. In that shape tree-sitter leaves
13933/// the declaration's `typedef` token as the sole child of the immediately
13934/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
13935/// declarator name inside the conditional's first declaration. The declaration
13936/// end is then the smallest structured boundary that contains the whole split
13937/// declaration.
13938#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13939pub struct CppDisplacedPreprocessorBoundary {
13940    pub end_byte: usize,
13941    pub end_line: usize,
13942}
13943
13944pub fn cpp_displaced_preprocessor_boundary(
13945    conditional: Node<'_>,
13946) -> Option<CppDisplacedPreprocessorBoundary> {
13947    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
13948        return Some(CppDisplacedPreprocessorBoundary {
13949            end_byte: terminator.end_byte(),
13950            end_line: terminator.end_position().row + 1,
13951        });
13952    }
13953    if let Some(declaration) = displaced_split_declaration(conditional) {
13954        return Some(CppDisplacedPreprocessorBoundary {
13955            end_byte: declaration.end_byte(),
13956            end_line: declaration.end_position().row + 1,
13957        });
13958    }
13959    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
13960        return Some(CppDisplacedPreprocessorBoundary {
13961            end_byte: terminator.end_byte(),
13962            end_line: terminator.end_position().row + 1,
13963        });
13964    }
13965    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
13966        return Some(CppDisplacedPreprocessorBoundary {
13967            end_byte: terminator.end_byte(),
13968            end_line: terminator.end_position().row + 1,
13969        });
13970    }
13971    None
13972}
13973
13974/// Recover an outer terminator that tree-sitter assigned to a damaged nested
13975/// conditional. This occurs when a split construct such as `extern "C"`
13976/// consumes the nested `#endif` inside an error node: the nested conditional's
13977/// direct terminator is then the outer conditional's real terminator, while
13978/// the outer node ends with a missing token and absorbs later declarations.
13979fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
13980    if !conditional.has_error()
13981        || conditional.child_by_field_name("alternative").is_some()
13982        || conditional
13983            .child(conditional.child_count().saturating_sub(1))
13984            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
13985    {
13986        return None;
13987    }
13988    let mut recovered = None;
13989    for nested in named_children_iter(conditional) {
13990        if !matches!(
13991            nested.kind(),
13992            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
13993        ) || nested.child_by_field_name("alternative").is_some()
13994        {
13995            continue;
13996        }
13997        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
13998            continue;
13999        };
14000        if direct.kind() != "#endif" || direct.is_missing() {
14001            continue;
14002        }
14003        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14004            continue;
14005        };
14006        if displaced.end_byte() >= direct.start_byte() {
14007            continue;
14008        }
14009        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14010            recovered = Some(direct);
14011        }
14012    }
14013    recovered
14014}
14015
14016fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14017    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14018        return None;
14019    }
14020    let mut cursor = conditional.walk();
14021    let declarations = conditional
14022        .named_children(&mut cursor)
14023        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14024        .collect::<Vec<_>>();
14025    let declaration = *declarations.first()?;
14026    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14027        return None;
14028    }
14029    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14030    let mut terminator = None;
14031    let mut stack = (0..declaration.child_count())
14032        .filter_map(|index| declaration.child(index))
14033        .filter(|child| child.start_byte() < declarator_start)
14034        .map(|child| (child, false))
14035        .collect::<Vec<_>>();
14036    while let Some((node, inside_error)) = stack.pop() {
14037        let inside_error = inside_error || node.kind() == "ERROR";
14038        if inside_error && node.kind() == "#endif" && !node.is_missing() {
14039            terminator = Some(node);
14040            continue;
14041        }
14042        for index in 0..node.child_count() {
14043            if let Some(child) = node.child(index)
14044                && child.start_byte() < declarator_start
14045            {
14046                stack.push((child, inside_error));
14047            }
14048        }
14049    }
14050    terminator
14051}
14052
14053fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14054    if !conditional.has_error()
14055        || conditional.child_by_field_name("alternative").is_some()
14056        || conditional
14057            .prev_named_sibling()
14058            .filter(|sibling| {
14059                sibling.kind() == "ERROR"
14060                    && sibling.child_count() == 1
14061                    && sibling
14062                        .child(0)
14063                        .is_some_and(|child| child.kind() == "typedef")
14064            })
14065            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14066            .is_none()
14067    {
14068        return None;
14069    }
14070    let mut cursor = conditional.walk();
14071    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14072    let declaration_index = children
14073        .iter()
14074        .position(|child| child.kind() == "declaration" && child.has_error())?;
14075    let declaration = children[declaration_index];
14076    if !children
14077        .iter()
14078        .skip(declaration_index + 1)
14079        .any(|child| child.end_byte() > declaration.end_byte())
14080    {
14081        return None;
14082    }
14083    let declarator = declaration.child_by_field_name("declarator")?;
14084    let mut error_end = None;
14085    let mut names = Vec::new();
14086    let mut stack = vec![declarator];
14087    while let Some(node) = stack.pop() {
14088        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14089            error_end =
14090                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14091            continue;
14092        }
14093        if matches!(node.kind(), "identifier" | "type_identifier") {
14094            names.push(node.start_byte());
14095        }
14096        push_named_children_reversed(node, &mut stack);
14097    }
14098    let error_end = error_end?;
14099    names
14100        .into_iter()
14101        .any(|start| start >= error_end)
14102        .then_some(declaration)
14103}
14104
14105fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14106    let Some(error) = parent.child(error_index) else {
14107        return false;
14108    };
14109    if error.kind() != "ERROR"
14110        || error.child_count() != 1
14111        || error.child(0).is_none_or(|child| child.kind() != "}")
14112    {
14113        return false;
14114    }
14115    let Some(semicolon) = parent.child(error_index + 1) else {
14116        return false;
14117    };
14118    semicolon.kind() == "expression_statement"
14119        && semicolon.child_count() == 1
14120        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14121}
14122
14123/// Locate the real end of a class-like declaration when a macro invocation
14124/// without a source semicolon absorbs the class's `};` into its parsed field.
14125/// The grammar then keeps following namespace declarations as later children
14126/// of the same field list. The direct ERROR-plus-semicolon pair proves the
14127/// boundary structurally; no source-text delimiter scan is needed.
14128fn displaced_macro_class_tail(
14129    declaration_node: Node<'_>,
14130    body: Node<'_>,
14131    source: &str,
14132) -> Option<DisplacedMacroClassTail> {
14133    if !matches!(
14134        declaration_node.kind(),
14135        "class_specifier" | "struct_specifier" | "union_specifier"
14136    ) || body.kind() != "field_declaration_list"
14137    {
14138        return None;
14139    }
14140
14141    let child_count = body.named_child_count();
14142    for index in 0..child_count {
14143        let child = body.named_child(index)?;
14144        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14145            continue;
14146        };
14147        let split_index = index + 1;
14148        if split_index >= child_count {
14149            return None;
14150        }
14151        let mut cursor = body.walk();
14152        if !body
14153            .named_children(&mut cursor)
14154            .skip(split_index)
14155            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14156        {
14157            return None;
14158        }
14159        return Some(DisplacedMacroClassTail {
14160            split_index,
14161            class_range: Range {
14162                start_byte: declaration_node.start_byte(),
14163                end_byte: terminator.end_byte(),
14164                start_line: declaration_node.start_position().row + 1,
14165                end_line: terminator.end_position().row + 1,
14166            },
14167        });
14168    }
14169    None
14170}
14171
14172fn displaced_macro_field_terminator<'tree>(
14173    field: Node<'tree>,
14174    source: &str,
14175) -> Option<Node<'tree>> {
14176    if field.kind() != "field_declaration" {
14177        return None;
14178    }
14179    // The older grammar reads the unterminated `MACRO(arg)` invocation as a
14180    // macro-token type and a `parenthesized_declarator`; the macro-declaration
14181    // grammar reads the whole invocation as a `macro_type_specifier`.
14182    let macro_type = field.child_by_field_name("type")?;
14183    let macro_name = match macro_type.kind() {
14184        "type_identifier"
14185            if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14186        {
14187            macro_type
14188        }
14189        "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14190        _ => return None,
14191    };
14192    if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14193        return None;
14194    }
14195    for index in 0..field.child_count() {
14196        let error = field.child(index)?;
14197        if error.kind() != "ERROR"
14198            || error.child_count() != 1
14199            || error.child(0).is_none_or(|child| child.kind() != "}")
14200        {
14201            continue;
14202        }
14203        let semicolon = field.child(index + 1)?;
14204        if semicolon.kind() == ";" {
14205            return Some(semicolon);
14206        }
14207    }
14208    None
14209}
14210
14211fn recover_fragmented_partial_specialization<'tree>(
14212    template_node: Node<'tree>,
14213    declaration_child: Node<'tree>,
14214    source: &str,
14215    ancestry: &ParentIndex<'tree>,
14216) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14217    if declaration_child.kind() != "function_definition" {
14218        return None;
14219    }
14220    let class_node = declaration_child.child_by_field_name("type")?;
14221    if !matches!(
14222        class_node.kind(),
14223        "class_specifier" | "struct_specifier" | "union_specifier"
14224    ) || !class_node
14225        .child_by_field_name("name")
14226        .and_then(|name| direct_identifier_name(name, source))
14227        .is_some_and(|name| cpp_export_macro_token(&name))
14228    {
14229        return None;
14230    }
14231    let declarator = declaration_child.child_by_field_name("declarator")?;
14232    if declarator.kind() != "template_function" {
14233        return None;
14234    }
14235    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14236    if metadata.specialization_arguments.is_empty() {
14237        return None;
14238    }
14239    let body = declaration_child.child_by_field_name("body")?;
14240    if body.kind() != "compound_statement" {
14241        return None;
14242    }
14243    let complete_prefix = body.named_child(0).filter(|first| {
14244        first.kind() == "labeled_statement"
14245            && first.has_error()
14246            && first
14247                .named_child(first.named_child_count().saturating_sub(1))
14248                .is_some_and(recovered_declaration_has_class_terminator)
14249    });
14250    let complete_body = complete_prefix.is_some();
14251    let mut prefix_members = Vec::new();
14252    if let Some(prefix) = complete_prefix {
14253        prefix_members.push(prefix);
14254    } else {
14255        let mut body_cursor = body.walk();
14256        for child in body.named_children(&mut body_cursor) {
14257            if !is_structurally_valid_fragmented_class_prefix_member(child) {
14258                break;
14259            }
14260            prefix_members.push(child);
14261        }
14262    }
14263    let containing_declarations = template_node.parent()?;
14264    if !matches!(
14265        containing_declarations.kind(),
14266        "declaration_list" | "compound_statement"
14267    ) {
14268        return None;
14269    }
14270    let mut member_siblings = Vec::new();
14271    let mut following_declarations = Vec::new();
14272    let terminator;
14273    if complete_body {
14274        terminator = complete_prefix?;
14275        let mut cursor = body.walk();
14276        let mut after_prefix = false;
14277        for child in body.named_children(&mut cursor) {
14278            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14279                after_prefix = true;
14280            } else if after_prefix {
14281                following_declarations.push(child);
14282            }
14283        }
14284    } else {
14285        let mut found_template = false;
14286        let mut cursor = containing_declarations.walk();
14287        let mut class_terminator = None;
14288        for child in containing_declarations.children(&mut cursor) {
14289            if same_node(child, template_node) {
14290                found_template = true;
14291                continue;
14292            }
14293            if found_template && child.kind() == "}" {
14294                class_terminator = Some(child);
14295                break;
14296            }
14297            // A namespace can never be a class member: reaching one before the
14298            // terminator proves the class's true close was swallowed upstream
14299            // and this scan has crossed into the enclosing scope, so the
14300            // recovery cannot be bounded -- continuing re-owns the namespace
14301            // block (and its template specializations) as class members under
14302            // a re-appended package, desyncing the fq boundary (#2306).
14303            if found_template && child.kind() == "namespace_definition" {
14304                return None;
14305            }
14306            if found_template && child.is_named() {
14307                member_siblings.push(child);
14308            }
14309        }
14310        terminator = class_terminator?;
14311    }
14312    let name = format!(
14313        "{}<{}>",
14314        metadata.primary_name,
14315        metadata
14316            .specialization_arguments
14317            .iter()
14318            .map(|argument| argument.text.as_str())
14319            .collect::<Vec<_>>()
14320            .join(", ")
14321    );
14322    Some(RecoveredFragmentedPartialSpecialization {
14323        declaration_node: declaration_child,
14324        name,
14325        range: Range {
14326            start_byte: declaration_child.start_byte(),
14327            end_byte: terminator.end_byte(),
14328            start_line: declaration_child.start_position().row + 1,
14329            end_line: terminator.end_position().row + 1,
14330        },
14331        prefix_members,
14332        member_siblings,
14333        following_declarations,
14334    })
14335}
14336
14337/// Whether `node` is the function-shaped parser wrapper whose body was
14338/// recovered as a fragmented partial-specialization class body.
14339///
14340/// Consumers that walk lexical function scopes must use the same complete
14341/// structural proof as declaration extraction. Otherwise member callables in
14342/// the recovered body are mistaken for function-local declarations.
14343pub fn is_recovered_fragmented_partial_specialization_container(
14344    node: Node<'_>,
14345    source: &str,
14346) -> bool {
14347    let Some(template) = node
14348        .parent()
14349        .filter(|parent| parent.kind() == "template_declaration")
14350    else {
14351        return false;
14352    };
14353    let mut root = template;
14354    while let Some(parent) = root.parent() {
14355        root = parent;
14356    }
14357    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14358        .is_some()
14359}
14360
14361fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14362    if declaration.kind() != "declaration" {
14363        return false;
14364    }
14365    // With an export macro between `class` and its name, tree-sitter folds a
14366    // complete class body into a function-shaped declaration. The class's own
14367    // `};` remains structurally identifiable as a direct ERROR child holding
14368    // `}`, immediately followed by the declaration's direct `;` child.
14369    (0..declaration.child_count().saturating_sub(1)).any(|index| {
14370        let Some(error) = declaration.child(index) else {
14371            return false;
14372        };
14373        error.kind() == "ERROR"
14374            && error.child_count() == 1
14375            && error.child(0).is_some_and(|child| child.kind() == "}")
14376            && declaration
14377                .child(index + 1)
14378                .is_some_and(|child| child.kind() == ";")
14379    })
14380}
14381
14382fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14383    if node.has_error() {
14384        return false;
14385    }
14386    match node.kind() {
14387        "declaration"
14388        | "field_declaration"
14389        | "alias_declaration"
14390        | "type_definition"
14391        | "static_assert_declaration" => true,
14392        "labeled_statement" => node
14393            .named_child(node.named_child_count().saturating_sub(1))
14394            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14395        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14396            matches!(
14397                child.kind(),
14398                "declaration"
14399                    | "field_declaration"
14400                    | "alias_declaration"
14401                    | "type_definition"
14402                    | "function_definition"
14403            )
14404        }),
14405        _ => false,
14406    }
14407}
14408
14409fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14410    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14411        .then(|| node.child_by_field_name("declarator"))
14412        .flatten()
14413        .and_then(|declarator| extract_variable_name(declarator, source))
14414}
14415
14416use crate::graph::syntax::has_function_scope_ancestor;
14417
14418fn cpp_template_metadata<'tree>(
14419    template_node: Node<'tree>,
14420    declaration_child: Node<'tree>,
14421    source: &str,
14422    ancestry: &ParentIndex<'tree>,
14423) -> Option<CppTemplateMetadata> {
14424    let parameters_node = template_node.child_by_field_name("parameters")?;
14425    let name_node = cpp_templated_class_name_node(declaration_child)?;
14426    let primary_node = match name_node.kind() {
14427        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14428        _ => name_node,
14429    };
14430    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14431    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14432        return None;
14433    }
14434
14435    let mut parameter_nodes = Vec::new();
14436    let mut parameter_names = Vec::new();
14437    let mut cursor = parameters_node.walk();
14438    for parameter in parameters_node.named_children(&mut cursor) {
14439        if !matches!(
14440            parameter.kind(),
14441            "type_parameter_declaration"
14442                | "optional_type_parameter_declaration"
14443                | "variadic_type_parameter_declaration"
14444                | "template_template_parameter_declaration"
14445                | "parameter_declaration"
14446                | "optional_parameter_declaration"
14447                | "variadic_parameter_declaration"
14448        ) {
14449            continue;
14450        }
14451        let index = parameter_nodes.len();
14452        // An unnamed parameter still contributes template arity and kind. Use
14453        // an impossible C++ identifier so positional reconciliation can bind
14454        // it without making source expressions refer to a name that was not
14455        // written.
14456        let name = cpp_template_parameter_name(parameter, source)
14457            .unwrap_or_else(|| format!("<anonymous:{index}>"));
14458        parameter_names.push(name);
14459        parameter_nodes.push(parameter);
14460    }
14461    let parameters = parameter_nodes
14462        .into_iter()
14463        .zip(parameter_names.iter().cloned())
14464        .map(|(parameter, name)| CppTemplateParameterMetadata {
14465            name,
14466            kind: cpp_template_parameter_kind(parameter),
14467            variadic: matches!(
14468                parameter.kind(),
14469                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14470            ),
14471            default: cpp_template_parameter_default_expression(
14472                parameter,
14473                source,
14474                &parameter_names,
14475                ancestry,
14476            ),
14477        })
14478        .collect();
14479    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14480        Vec::new()
14481    } else {
14482        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
14483            .unwrap_or_default()
14484    };
14485    let alias_target = (declaration_child.kind() == "alias_declaration")
14486        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
14487        .flatten();
14488    Some(CppTemplateMetadata {
14489        primary_name,
14490        primary_fq_name: String::new(),
14491        parameters,
14492        specialization_arguments,
14493        alias_target,
14494    })
14495}
14496
14497fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14498    match node.kind() {
14499        "class_specifier" | "struct_specifier" | "union_specifier" => {
14500            node.child_by_field_name("name")
14501        }
14502        "function_definition" => {
14503            let declarator = node.child_by_field_name("declarator")?;
14504            if matches!(declarator.kind(), "identifier" | "template_function") {
14505                Some(declarator)
14506            } else {
14507                None
14508            }
14509        }
14510        "alias_declaration" => node.child_by_field_name("name"),
14511        _ => None,
14512    }
14513}
14514
14515fn cpp_template_alias_target<'tree>(
14516    alias: Node<'tree>,
14517    source: &str,
14518    parameter_names: &[String],
14519    ancestry: &ParentIndex<'tree>,
14520) -> Option<CppTemplateAliasTargetMetadata> {
14521    let mut type_node = alias.child_by_field_name("type")?;
14522    while type_node.kind() == "type_descriptor" {
14523        type_node = type_node.child_by_field_name("type")?;
14524    }
14525    let global = type_node.child_by_field_name("scope").is_none()
14526        && type_node.child(0).is_some_and(|child| child.kind() == "::");
14527    let mut components = Vec::new();
14528    cpp_template_target_components(type_node, source, &mut components)?;
14529    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14530    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14531        components,
14532        global,
14533        arguments,
14534    })
14535}
14536
14537fn cpp_template_target_components(
14538    node: Node<'_>,
14539    source: &str,
14540    out: &mut Vec<String>,
14541) -> Option<()> {
14542    match node.kind() {
14543        "identifier" | "namespace_identifier" | "type_identifier" => {
14544            out.push(node_text(node, source).to_string());
14545            Some(())
14546        }
14547        "template_type" => {
14548            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
14549        }
14550        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14551            if let Some(scope) = node.child_by_field_name("scope") {
14552                cpp_template_target_components(scope, source, out)?;
14553            }
14554            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
14555        }
14556        _ => None,
14557    }
14558}
14559
14560fn cpp_template_argument_expressions<'tree>(
14561    mut node: Node<'tree>,
14562    source: &str,
14563    parameter_names: &[String],
14564    ancestry: &ParentIndex<'tree>,
14565) -> Option<Vec<CppTemplateExpression>> {
14566    loop {
14567        match node.kind() {
14568            "template_type" | "template_function" => {
14569                let arguments = node.child_by_field_name("arguments")?;
14570                let mut cursor = arguments.walk();
14571                return Some(
14572                    arguments
14573                        .named_children(&mut cursor)
14574                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14575                        .map(|argument| {
14576                            cpp_template_expression(argument, source, parameter_names, ancestry)
14577                        })
14578                        .collect(),
14579                );
14580            }
14581            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14582                node = node
14583                    .child_by_field_name("name")
14584                    .or_else(|| node.child_by_field_name("type"))?;
14585            }
14586            _ => return None,
14587        }
14588    }
14589}
14590
14591fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14592    let candidate = node
14593        .child_by_field_name("name")
14594        .or_else(|| node.child_by_field_name("declarator"))
14595        .or_else(|| cpp_recovered_type_parameter_name(node))
14596        .or_else(|| {
14597            let mut cursor = node.walk();
14598            node.named_children(&mut cursor).find(|child| {
14599                matches!(
14600                    child.kind(),
14601                    "identifier" | "type_identifier" | "field_identifier"
14602                )
14603            })
14604        })?;
14605    if let Some(name) = direct_identifier_name(candidate, source) {
14606        return Some(name);
14607    }
14608    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14609    (!name.is_empty()).then_some(name)
14610}
14611
14612fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14613    match node.kind() {
14614        "type_parameter_declaration"
14615        | "optional_type_parameter_declaration"
14616        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14617        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14618        _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14619        _ => CppTemplateParameterKind::Value,
14620    }
14621}
14622
14623/// The name of a defaulted type parameter that tree-sitter-cpp recovered as a
14624/// non-type parameter. The grammar's `default_type` cannot hold an abstract
14625/// declarator, so `typename U = T*` always recovers the `*` as an ERROR
14626/// sibling; since grammar 0.24.4 the parameter itself also becomes an
14627/// `optional_parameter_declaration` with `type: dependent_type(typename U)`,
14628/// no declarator and `default_value: T`. A valid dependent type names a
14629/// qualified type after `typename`, so a bare type identifier there is the
14630/// type parameter's name.
14631pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14632    if parameter.kind() != "optional_parameter_declaration"
14633        || parameter.child_by_field_name("declarator").is_some()
14634    {
14635        return None;
14636    }
14637    let dependent = parameter
14638        .child_by_field_name("type")
14639        .filter(|type_node| type_node.kind() == "dependent_type")?;
14640    let mut cursor = dependent.walk();
14641    let mut named = dependent
14642        .named_children(&mut cursor)
14643        .filter(|child| !child.is_extra());
14644    match (named.next(), named.next()) {
14645        (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14646        _ => None,
14647    }
14648}
14649
14650#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14651pub struct CppTemplateParameterNameMatches {
14652    pub any_parameter: bool,
14653    pub type_parameter: bool,
14654}
14655
14656pub fn cpp_template_parameter_name_matches(
14657    parameters: Node<'_>,
14658    name: &str,
14659    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14660) -> CppTemplateParameterNameMatches {
14661    let mut matches = CppTemplateParameterNameMatches::default();
14662    let mut cursor = parameters.walk();
14663    for parameter in parameters.named_children(&mut cursor) {
14664        if parameter_name(parameter).as_deref() == Some(name) {
14665            matches.any_parameter = true;
14666            matches.type_parameter |=
14667                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14668        }
14669    }
14670    matches
14671}
14672
14673pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14674    parent.child_by_field_name("type").is_some_and(|type_node| {
14675        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14676    })
14677}
14678
14679fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14680    node.child_by_field_name("default_type")
14681        .or_else(|| node.child_by_field_name("default_value"))
14682}
14683
14684fn cpp_template_parameter_default_expression<'tree>(
14685    parameter: Node<'tree>,
14686    source: &str,
14687    parameter_names: &[String],
14688    ancestry: &ParentIndex<'tree>,
14689) -> Option<CppTemplateExpression> {
14690    let default = cpp_template_parameter_default(parameter)?;
14691    let base = cpp_template_expression(default, source, parameter_names, ancestry);
14692    let Some(pointer_error) = parameter.next_named_sibling() else {
14693        return Some(base);
14694    };
14695    let Some(pointer_declarator) =
14696        recovered_abstract_pointer_declarator_term(pointer_error, source)
14697    else {
14698        return Some(base);
14699    };
14700    Some(CppTemplateExpression {
14701        text: format!(
14702            "{}{}",
14703            base.text,
14704            normalize_cpp_whitespace(node_text(pointer_error, source))
14705        ),
14706        term: CppTemplateTerm::Node {
14707            kind: "type_descriptor".to_string(),
14708            children: vec![base.term, pointer_declarator],
14709        },
14710    })
14711}
14712
14713fn recovered_abstract_pointer_declarator_term(
14714    node: Node<'_>,
14715    source: &str,
14716) -> Option<CppTemplateTerm> {
14717    if node.kind() != "ERROR" || node.child_count() == 0 {
14718        return None;
14719    }
14720    let mut children = Vec::new();
14721    for index in 0..node.child_count() {
14722        let child = node.child(index)?;
14723        if child.kind() != "*" {
14724            return None;
14725        }
14726        children.push(CppTemplateTerm::Atom {
14727            kind: "*".to_string(),
14728            text: normalize_cpp_whitespace(node_text(child, source)),
14729        });
14730    }
14731    Some(CppTemplateTerm::Node {
14732        kind: "abstract_pointer_declarator".to_string(),
14733        children,
14734    })
14735}
14736
14737fn cpp_template_expression<'tree>(
14738    node: Node<'tree>,
14739    source: &str,
14740    parameter_names: &[String],
14741    ancestry: &ParentIndex<'tree>,
14742) -> CppTemplateExpression {
14743    let text = normalize_cpp_whitespace(node_text(node, source));
14744    CppTemplateExpression {
14745        text,
14746        term: cpp_template_term(node, source, parameter_names, ancestry),
14747    }
14748}
14749
14750pub fn cpp_template_term<'tree>(
14751    node: Node<'tree>,
14752    source: &str,
14753    parameter_names: &[String],
14754    ancestry: &ParentIndex<'tree>,
14755) -> CppTemplateTerm {
14756    enum Work<'tree> {
14757        Visit(Node<'tree>),
14758        Build { kind: String, child_count: usize },
14759    }
14760
14761    let mut work = vec![Work::Visit(node)];
14762    let mut terms = Vec::new();
14763    while let Some(next) = work.pop() {
14764        match next {
14765            Work::Visit(current) => {
14766                let text = normalize_cpp_whitespace(node_text(current, source));
14767                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
14768                    terms.push(CppTemplateTerm::Parameter(text));
14769                    continue;
14770                }
14771                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
14772                    let mut cursor = current.walk();
14773                    let named = current
14774                        .named_children(&mut cursor)
14775                        .filter(|child| !child.is_extra() && child.kind() != "comment")
14776                        .collect::<Vec<_>>();
14777                    if let [child] = named.as_slice() {
14778                        work.push(Work::Visit(*child));
14779                        continue;
14780                    }
14781                }
14782                if current.child_count() == 0 {
14783                    terms.push(CppTemplateTerm::Atom {
14784                        kind: if matches!(
14785                            current.kind(),
14786                            "identifier"
14787                                | "type_identifier"
14788                                | "field_identifier"
14789                                | "namespace_identifier"
14790                        ) {
14791                            "identifier".to_string()
14792                        } else {
14793                            current.kind().to_string()
14794                        },
14795                        text,
14796                    });
14797                    continue;
14798                }
14799                let children = (0..current.child_count())
14800                    .filter_map(|index| current.child(index))
14801                    .filter(|child| !child.is_extra() && child.kind() != "comment")
14802                    .collect::<Vec<_>>();
14803                work.push(Work::Build {
14804                    kind: current.kind().to_string(),
14805                    child_count: children.len(),
14806                });
14807                work.extend(children.into_iter().rev().map(Work::Visit));
14808            }
14809            Work::Build { kind, child_count } => {
14810                let children = terms.split_off(terms.len() - child_count);
14811                terms.push(CppTemplateTerm::Node { kind, children });
14812            }
14813        }
14814    }
14815    terms.pop().expect("template term traversal emits one root")
14816}
14817
14818fn cpp_template_term_leaf_is_parameter<'tree>(
14819    node: Node<'tree>,
14820    text: &str,
14821    parameter_names: &[String],
14822    ancestry: &ParentIndex<'tree>,
14823) -> bool {
14824    if !parameter_names.iter().any(|parameter| parameter == text) {
14825        return false;
14826    }
14827    !ancestry.parent(node).is_some_and(|parent| {
14828        matches!(
14829            parent.kind(),
14830            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
14831        ) && parent.child_by_field_name("scope").is_some()
14832            && parent.child_by_field_name("name") == Some(node)
14833    })
14834}
14835
14836fn enclosing_cpp_declaration_node<'tree>(
14837    mut node: Node<'tree>,
14838    ancestry: &ParentIndex<'tree>,
14839) -> Option<Node<'tree>> {
14840    loop {
14841        match node.kind() {
14842            "declaration"
14843            | "function_declaration"
14844            | "field_declaration"
14845            | "function_definition" => return Some(node),
14846            _ => node = ancestry.parent(node)?,
14847        }
14848    }
14849}
14850
14851fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
14852    let mut params = Vec::new();
14853    let mut cursor = parameters_node.walk();
14854    for child in parameters_node.children(&mut cursor) {
14855        match child.kind() {
14856            "explicit_object_parameter_declaration" => {
14857                if let Some(parameter) = child
14858                    .named_children(&mut child.walk())
14859                    .find(|node| node.kind() == "parameter_declaration")
14860                {
14861                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
14862                }
14863            }
14864            "parameter_declaration" | "optional_parameter_declaration" => {
14865                params.push(cpp_parameter_type(child, source));
14866            }
14867            "variadic_parameter_declaration" => {
14868                params.push(cpp_parameter_type(child, source));
14869            }
14870            "variadic_parameter" | "..." => params.push("...".to_string()),
14871            _ => {}
14872        }
14873    }
14874
14875    if params.is_empty() {
14876        "()".to_string()
14877    } else {
14878        format!("({})", params.join(", "))
14879    }
14880}
14881
14882/// Where a C or C++ callable declaration is written, which is what decides
14883/// which modifier facts its own node is able to state.
14884enum CppCallableScope<'tree> {
14885    /// Written directly in a class, struct or union body. The declaration
14886    /// states its own storage class, and its access is the one the body's
14887    /// specifier ladder is under at that point.
14888    ClassBody {
14889        body: Node<'tree>,
14890        default_visibility: DeclaredVisibility,
14891    },
14892    /// A qualified declaration written outside every class body
14893    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
14894    /// writes no access specifier, and whether the qualifier names a class or
14895    /// a namespace is not decidable from this file alone -- that is exactly
14896    /// what [`crate::reconcile`] needs the include-visible class table for.
14897    Qualified,
14898    /// Namespace scope or file scope, reached by an unqualified name. C has no
14899    /// member functions at all, so every C callable lands here.
14900    Unqualified,
14901}
14902
14903/// Where the declaration sits, from one upward walk of its own ancestry.
14904///
14905/// The walk stops as soon as something proves the declaration is outside every
14906/// class body: a `namespace_definition`, because a class body never encloses
14907/// one, and a `compound_statement`, because a body opens a block scope and
14908/// nothing declared in it is a member of the class the enclosing definition
14909/// belongs to. That is the same rule
14910/// `CppVisitor::visit_anonymous_local_aggregates_in_function` applies when it
14911/// drops the aggregate carrier, and it keeps this question off the O(depth)
14912/// walk a deeply nested generated header would otherwise pay per callable.
14913///
14914/// `function_declarator` is `None` for a declaration whose callable the macro
14915/// recovery reparsed out of a malformed member: there is no declarator node to
14916/// ask whether the name carries a qualifier, and a recovered member is written
14917/// in its class body anyway.
14918fn cpp_callable_scope<'tree>(
14919    declaration: Node<'tree>,
14920    function_declarator: Option<Node<'tree>>,
14921    ancestry: &ParentIndex<'tree>,
14922) -> CppCallableScope<'tree> {
14923    let mut current = Some(declaration);
14924    while let Some(node) = current {
14925        match node.kind() {
14926            "field_declaration_list" => {
14927                let default_visibility =
14928                    ancestry
14929                        .parent(node)
14930                        .map_or(DeclaredVisibility::Unknown, |class_like| {
14931                            // `struct` and `union` members default to public;
14932                            // `class` members default to private.
14933                            match class_like.kind() {
14934                                "struct_specifier" | "union_specifier" => {
14935                                    DeclaredVisibility::Public
14936                                }
14937                                _ => DeclaredVisibility::Private,
14938                            }
14939                        });
14940                return CppCallableScope::ClassBody {
14941                    body: node,
14942                    default_visibility,
14943                };
14944            }
14945            "namespace_definition" | "compound_statement" | "translation_unit" => break,
14946            _ => {}
14947        }
14948        current = ancestry.parent(node);
14949    }
14950    if cpp_callable_declarator_is_qualified(function_declarator) {
14951        CppCallableScope::Qualified
14952    } else {
14953        CppCallableScope::Unqualified
14954    }
14955}
14956
14957/// Whether the declarator's own written name carries a `::` qualifier.
14958///
14959/// A qualified declaration written outside every class body
14960/// (`long Client::send(int) {}`) names a member whose class body this file may
14961/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
14962/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
14963fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
14964    function_declarator
14965        .and_then(cpp_function_declarator_name_node)
14966        .is_some_and(|name| name.kind() == "qualified_identifier")
14967}
14968
14969/// The access one class-body member declaration is written under.
14970///
14971/// C++ states member access positionally: an `access_specifier` applies to
14972/// every member written after it until the next one, and the members before
14973/// the first one take the class keyword's default. The answer is therefore the
14974/// last specifier the body writes before the member's own list-level node,
14975/// read from the specifier's own keyword token rather than from its text. A
14976/// label a macro invocation swallowed has no specifier node or keyword token;
14977/// [`cpp_macro_swallowed_access_label`] reads it from the declarator the
14978/// grammar made of the keyword.
14979///
14980/// The body's children are read forward with one cursor rather than backwards
14981/// from the member: `Node::prev_sibling` re-descends from the root for each
14982/// step, which is the per-edge cost #2361 keeps off this walk.
14983fn cpp_member_declared_visibility<'tree>(
14984    declaration: Node<'tree>,
14985    body: Node<'tree>,
14986    default_visibility: DeclaredVisibility,
14987    source: &str,
14988    ancestry: &ParentIndex<'tree>,
14989) -> DeclaredVisibility {
14990    let mut list_level = declaration;
14991    while ancestry.parent(list_level) != Some(body) {
14992        let Some(parent) = ancestry.parent(list_level) else {
14993            return default_visibility;
14994        };
14995        list_level = parent;
14996    }
14997    let mut visibility = default_visibility;
14998    let mut cursor = body.walk();
14999    for child in body.children(&mut cursor) {
15000        if child == list_level {
15001            break;
15002        }
15003        let access = if child.kind() == "access_specifier" {
15004            child.child(0).map(|keyword| keyword.kind())
15005        } else {
15006            cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15007        };
15008        visibility = match access {
15009            Some("public") => DeclaredVisibility::Public,
15010            Some("protected") => DeclaredVisibility::Protected,
15011            Some("private") => DeclaredVisibility::Private,
15012            _ => visibility,
15013        };
15014    }
15015    visibility
15016}
15017
15018/// Whether this declaration writes the `static` storage class specifier.
15019///
15020/// What `static` *means* is the scope's business: on a class-body member it is
15021/// the receiver fact, and at file scope it is internal linkage, which is why
15022/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
15023/// question and answer it differently.
15024fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15025    let mut cursor = declaration.walk();
15026    declaration.named_children(&mut cursor).any(|child| {
15027        child.kind() == "storage_class_specifier"
15028            && normalize_cpp_whitespace(node_text(child, source)) == "static"
15029    })
15030}
15031
15032/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
15033/// publishes, as C and C++ state them.
15034struct CppCallableModifiers {
15035    is_static: bool,
15036    is_constructor: bool,
15037    visibility: DeclaredVisibility,
15038}
15039
15040/// The modifier facts a C or C++ callable declaration states in its own shape.
15041///
15042/// C++ has no `static` keyword for receivers and no visibility keyword of the
15043/// Java kind: it states both in the declaration's position. A member declared
15044/// in a class body binds the object it is selected on unless that declaration
15045/// writes the `static` storage class, and its access is the one the body's
15046/// specifier ladder is under. A function at namespace or file scope binds
15047/// nothing and has no access specifier at all, so its declared visibility is
15048/// Unknown. Linkage is recorded separately and does not declare access. C has
15049/// no member functions, and the `static` a C function writes is internal linkage,
15050/// never a receiver fact.
15051///
15052/// Recording these is what lets `receiver_contract_of` answer for a C or C++
15053/// declaration. Without them it reported no contract at all,
15054/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
15055/// callable, no reviewed summary could name one, and every effect or taint walk
15056/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
15057/// in #3455, Kotlin in #3453, Python in #3451.
15058///
15059/// An out-of-line member definition cannot repeat `static` or access. These
15060/// are per-file facts only: the analyzer reconciles the exact overload with
15061/// include-visible class declarations before exposing its receiver contract
15062/// (#3509). A missing declaration leaves that contract unknown.
15063fn cpp_callable_modifiers<'tree>(
15064    declaration: Node<'tree>,
15065    function_declarator: Option<Node<'tree>>,
15066    is_constructor: bool,
15067    source: &str,
15068    ancestry: &ParentIndex<'tree>,
15069) -> CppCallableModifiers {
15070    match cpp_callable_scope(declaration, function_declarator, ancestry) {
15071        CppCallableScope::ClassBody {
15072            body,
15073            default_visibility,
15074        } => CppCallableModifiers {
15075            is_static: cpp_declares_static_storage_class(declaration, source),
15076            is_constructor,
15077            visibility: cpp_member_declared_visibility(
15078                declaration,
15079                body,
15080                default_visibility,
15081                source,
15082                ancestry,
15083            ),
15084        },
15085        CppCallableScope::Qualified => CppCallableModifiers {
15086            is_static: false,
15087            is_constructor,
15088            visibility: DeclaredVisibility::Unknown,
15089        },
15090        CppCallableScope::Unqualified => CppCallableModifiers {
15091            is_static: false,
15092            is_constructor,
15093            visibility: DeclaredVisibility::Unknown,
15094        },
15095    }
15096}
15097
15098/// The persisted signature facts one C or C++ callable declaration publishes.
15099///
15100/// `linkage` is the caller's, not this function's: the node that carries the
15101/// storage class is not always the declaration this declarator sits in -- a
15102/// recovered constructor reads it from its class declaration, and a
15103/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
15104/// visit site that knows which node states it passes the answer in. Recording
15105/// it here keeps it beside the declared visibility it also decides.
15106fn cpp_signature_metadata<'tree>(
15107    signature: String,
15108    function_declarator: Node<'tree>,
15109    linkage: CallableLinkage,
15110    source: &str,
15111    ancestry: &ParentIndex<'tree>,
15112) -> SignatureMetadata {
15113    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15114    let is_constructor =
15115        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15116    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15117        Some(declaration) => cpp_callable_modifiers(
15118            declaration,
15119            Some(function_declarator),
15120            is_constructor,
15121            source,
15122            ancestry,
15123        ),
15124        // Severe recovery left no declaration node above this declarator, so
15125        // nothing is stated here beyond what the callable's own name proves.
15126        None => CppCallableModifiers {
15127            is_static: false,
15128            is_constructor,
15129            visibility: DeclaredVisibility::Unknown,
15130        },
15131    };
15132    let enrich = |mut metadata: SignatureMetadata| {
15133        // #3507: a qualified definition repeats none of the class body's
15134        // dispatch facts, so this row publishes none either.
15135        if let Some(dispatch) = dispatch {
15136            metadata = metadata.with_dispatch_extensibility(dispatch);
15137        }
15138        metadata
15139            .with_callable_linkage(linkage)
15140            .with_callable_modifiers(
15141                modifiers.is_static,
15142                modifiers.is_constructor,
15143                modifiers.visibility,
15144            )
15145    };
15146    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15147    let return_type_identity =
15148        cpp_callable_return_type_identity(function_declarator, source, ancestry);
15149    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15150        return enrich(
15151            SignatureMetadata::new(signature, Vec::new())
15152                .with_return_type_text(return_type_text)
15153                .with_return_type_identity(return_type_identity),
15154        );
15155    };
15156    let callable_arity = cpp_callable_arity(parameters_node, source);
15157    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15158    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15159    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15160    let Some(relative_start) = signature
15161        .get(search_from..)
15162        .and_then(|suffix| suffix.find(&parameter_text))
15163    else {
15164        return enrich(
15165            SignatureMetadata::new(signature, Vec::new())
15166                .with_callable_arity(callable_arity)
15167                .with_callable_parameter_types(callable_parameter_types)
15168                .with_return_type_text(return_type_text)
15169                .with_return_type_identity(return_type_identity),
15170        );
15171    };
15172    let parameters_start = search_from + relative_start;
15173    let parameters_end = parameters_start + parameter_text.len();
15174    let mut search_start = parameters_start;
15175    let parameters = cpp_parameter_label_nodes(parameters_node)
15176        .into_iter()
15177        .filter_map(|label_node| {
15178            let label = normalize_cpp_whitespace(node_text(label_node, source));
15179            if label.is_empty() || search_start > parameters_end {
15180                return None;
15181            }
15182            let haystack = signature.get(search_start..parameters_end)?;
15183            let relative_start = haystack.find(&label)?;
15184            let start_byte = search_start + relative_start;
15185            let end_byte = start_byte + label.len();
15186            search_start = end_byte;
15187            Some(ParameterMetadata::new(label, start_byte, end_byte))
15188        })
15189        .collect();
15190    enrich(
15191        SignatureMetadata::new(signature, parameters)
15192            .with_callable_arity(callable_arity)
15193            .with_callable_parameter_types(callable_parameter_types)
15194            .with_return_type_text(return_type_text)
15195            .with_return_type_identity(return_type_identity),
15196    )
15197}
15198
15199pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15200    function_declarator: Node<'tree>,
15201    source: &str,
15202    ancestry: &ParentIndex<'tree>,
15203) -> bool {
15204    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15205        .or_else(|| function_declarator.child_by_field_name("name"))
15206        .or_else(|| last_named_child(function_declarator))
15207    else {
15208        return false;
15209    };
15210    // An out-of-line definition writes the class in the name itself
15211    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
15212    // terminal component answers without any enclosing class body.
15213    if name_node.kind() == "qualified_identifier" {
15214        let owner = name_node
15215            .child_by_field_name("scope")
15216            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15217        let member = name_node
15218            .child_by_field_name("name")
15219            .and_then(|name| canonical_cpp_qualified_component(name, source));
15220        return match (owner, member) {
15221            (Some(owner), Some(member)) => owner.name == member.name,
15222            _ => false,
15223        };
15224    }
15225    let Some(callable_name) = direct_identifier_name(name_node, source) else {
15226        return false;
15227    };
15228
15229    let mut current = ancestry.parent(function_declarator);
15230    while let Some(ancestor) = current {
15231        let owner_name = match ancestor.kind() {
15232            "class_specifier" | "struct_specifier" | "union_specifier" => {
15233                class_like_name(ancestor, source, ancestry)
15234            }
15235            "ERROR" => malformed_class_error_owner_name(ancestor, source),
15236            _ => None,
15237        };
15238        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15239            return true;
15240        }
15241        current = ancestry.parent(ancestor);
15242    }
15243    false
15244}
15245
15246/// Recover the owner name from the direct grammar shape retained when a later
15247/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
15248/// `ERROR` node:
15249///
15250/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
15251///
15252/// Direct-child checks keep this distinct from an unrelated nested class inside
15253/// a broader error region. The closing brace may be displaced past the error
15254/// node, so the opening body token is the available structural boundary.
15255fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15256    if node.kind() != "ERROR" {
15257        return None;
15258    }
15259    let keyword = node.child(0)?;
15260    if !matches!(keyword.kind(), "class" | "struct" | "union") {
15261        return None;
15262    }
15263    let name_node = node.child(1)?;
15264    let name = direct_identifier_name(name_node, source)?;
15265    let has_body = (2..node.child_count())
15266        .filter_map(|index| node.child(index))
15267        .any(|child| child.kind() == "{");
15268    has_body.then_some(name)
15269}
15270
15271/// Whether one declaration carries a native must-use attribute.
15272///
15273/// Attribute names are read only from structurally selected attribute nodes;
15274/// an identifier with the same spelling elsewhere in the declaration cannot
15275/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
15276/// while standard `nodiscard` is accepted without a namespace prefix.
15277pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15278    // The C++ grammar keeps a prefix attribute on the enclosing declaration
15279    // while the enum unit's occurrence points at its `enum_specifier`. Bring
15280    // that one structural sibling into the same declaration boundary; this
15281    // does not widen the walk to surrounding declarations.
15282    let declaration = declaration
15283        .parent()
15284        .filter(|parent| {
15285            parent.kind() == "declaration"
15286                && parent
15287                    .child_by_field_name("type")
15288                    .is_some_and(|type_node| type_node.id() == declaration.id())
15289        })
15290        .unwrap_or(declaration);
15291    let mut pending = vec![declaration];
15292    while let Some(node) = pending.pop() {
15293        if cpp_attribute_node_is_native_must_use(node, source)
15294            && cpp_attribute_is_attached_to_declaration(node, declaration)
15295        {
15296            return true;
15297        }
15298        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15299        {
15300            continue;
15301        }
15302        push_named_children_reversed(node, &mut pending);
15303    }
15304    false
15305}
15306
15307fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15308    if node.kind() == "attribute" {
15309        return node.child_by_field_name("name").is_some_and(|name| {
15310            let name = node_text(name, source);
15311            let prefix = node
15312                .child_by_field_name("prefix")
15313                .map(|prefix| node_text(prefix, source));
15314            matches!(
15315                (prefix, name),
15316                (None, "nodiscard")
15317                    | (None, "warn_unused_result")
15318                    | (Some("gnu"), "warn_unused_result")
15319            )
15320        });
15321    }
15322    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15323    {
15324        return false;
15325    }
15326    let Some(arguments) = node.named_child(0) else {
15327        return false;
15328    };
15329    let Some(name) = arguments.named_child(0) else {
15330        return false;
15331    };
15332    arguments.kind() == "argument_list"
15333        && name.kind() == "identifier"
15334        && node_text(name, source) == "warn_unused_result"
15335}
15336
15337fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15338    let mut current = attribute;
15339    while let Some(parent) = current.parent() {
15340        if parent.id() == declaration.id() {
15341            return true;
15342        }
15343        if matches!(
15344            parent.kind(),
15345            "declaration"
15346                | "field_declaration"
15347                | "function_definition"
15348                | "struct_specifier"
15349                | "class_specifier"
15350                | "union_specifier"
15351                | "enum_specifier"
15352        ) {
15353            return false;
15354        }
15355        current = parent;
15356    }
15357    false
15358}
15359
15360/// Whether export/decorator macros ahead of a callable declaration's `type`
15361/// make that type head decoration rather than a semantic return type. The
15362/// older grammar leaves the macro token in the `type` field and the real type
15363/// in an ERROR sibling; the macro-declaration grammar wraps the same token as a
15364/// leading `macro_modifier`, or the whole invocation as a
15365/// `macro_type_specifier` type. Such a declaration does not carry enough
15366/// structured evidence to persist a return type, as text or as identity.
15367fn cpp_declaration_return_type_is_macro_decorated(
15368    declaration: Node<'_>,
15369    type_node: Node<'_>,
15370    source: &str,
15371) -> bool {
15372    type_node.kind() == "macro_type_specifier"
15373        || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15374        || (cpp_export_macro_token(node_text(type_node, source))
15375            && (0..declaration.named_child_count()).any(|index| {
15376                declaration
15377                    .named_child(index)
15378                    .is_some_and(|child| child.kind() == "ERROR")
15379            }))
15380}
15381
15382/// Whether a `macro_modifier` precedes `type_node` among `declaration`'s
15383/// specifiers: the macro-declaration grammar's clean reading of an unexpanded
15384/// decorator macro ahead of the declared type (`MACRO static inline T f()`).
15385pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15386    declaration: Node<'_>,
15387    type_node: Node<'_>,
15388) -> bool {
15389    let mut head_cursor = declaration.walk();
15390    declaration
15391        .named_children(&mut head_cursor)
15392        .take_while(|child| *child != type_node)
15393        .any(|child| child.kind() == "macro_modifier")
15394}
15395
15396fn cpp_callable_return_type_parts<'tree>(
15397    function_declarator: Node<'tree>,
15398    source: &str,
15399    ancestry: &ParentIndex<'tree>,
15400) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15401    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15402        return None;
15403    }
15404    if let Some((return_type, _)) =
15405        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15406    {
15407        return Some((return_type, Vec::new()));
15408    }
15409    let mut cursor = function_declarator.walk();
15410    if let Some(trailing) = function_declarator
15411        .named_children(&mut cursor)
15412        .find(|child| child.kind() == "trailing_return_type")
15413        && let Some(type_descriptor) = trailing.named_child(0)
15414    {
15415        return Some((type_descriptor, Vec::new()));
15416    }
15417
15418    let mut current = function_declarator;
15419    let mut wrappers = Vec::new();
15420    while let Some(parent) = ancestry.parent(current) {
15421        if matches!(
15422            parent.kind(),
15423            "function_definition" | "declaration" | "field_declaration"
15424        ) {
15425            let type_node = parent.child_by_field_name("type")?;
15426            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15427                return None;
15428            }
15429            return Some((type_node, wrappers));
15430        }
15431        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15432            || (matches!(
15433                parent.kind(),
15434                "pointer_declarator"
15435                    | "reference_declarator"
15436                    | "array_declarator"
15437                    | "parenthesized_declarator"
15438            ) && parent.named_child_count() == 1
15439                && parent.named_child(0) == Some(current));
15440        if !wraps_current_declarator {
15441            return None;
15442        }
15443        match parent.kind() {
15444            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15445            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15446            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15447            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15448            _ => return None,
15449        }
15450        current = parent;
15451    }
15452    None
15453}
15454
15455/// Return the AST node that describes one callable's declared result type.
15456///
15457/// The node is returned only after the same declarator walk that establishes
15458/// the persisted C++ return identity. Consumers can therefore resolve a
15459/// nominal result or inspect its declaration attributes without parsing a
15460/// rendered signature.
15461pub fn cpp_callable_declaration_return_type_node<'tree>(
15462    callable: Node<'tree>,
15463    source: &str,
15464    ancestry: &ParentIndex<'tree>,
15465) -> Option<Node<'tree>> {
15466    let declarator = callable
15467        .child_by_field_name("declarator")
15468        .and_then(extract_function_declarator)?;
15469    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15470}
15471
15472/// Return the structured declared result type of one callable declaration.
15473///
15474/// This accepts the enclosing declaration node so consumers do not need to
15475/// duplicate the declarator-unwrapping rules before asking for the structured
15476/// identity.
15477pub fn cpp_callable_declaration_return_type_identity<'tree>(
15478    callable: Node<'tree>,
15479    source: &str,
15480    ancestry: &ParentIndex<'tree>,
15481) -> Option<StructuredTypeIdentity> {
15482    let declarator = callable
15483        .child_by_field_name("declarator")
15484        .and_then(extract_function_declarator)?;
15485    cpp_callable_return_type_identity(declarator, source, ancestry)
15486}
15487
15488pub(crate) fn cpp_callable_return_type_identity<'tree>(
15489    function_declarator: Node<'tree>,
15490    source: &str,
15491    ancestry: &ParentIndex<'tree>,
15492) -> Option<StructuredTypeIdentity> {
15493    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15494    let (return_type, wrappers) =
15495        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15496    cpp_apply_structured_type_wrappers(
15497        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15498        wrappers,
15499    )
15500}
15501
15502fn cpp_structured_type_identity(
15503    node: Node<'_>,
15504    source: &str,
15505    lexical_scope: &[String],
15506) -> Option<StructuredTypeIdentity> {
15507    enum Work<'tree> {
15508        Visit(Node<'tree>),
15509        Wrap(CppStructuredTypeWrapper),
15510        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15511        BuildGeneric { argument_count: usize },
15512    }
15513
15514    let mut work = vec![Work::Visit(node)];
15515    let mut values = Vec::new();
15516    let mut builder = StructuredTypeIdentityBuilder::default();
15517    while let Some(next) = work.pop() {
15518        match next {
15519            Work::Visit(current) => match current.kind() {
15520                "type_descriptor" => {
15521                    let type_node = current
15522                        .child_by_field_name("type")
15523                        .or_else(|| current.named_child(0))?;
15524                    let mut wrappers = Vec::new();
15525                    let mut cursor = current.walk();
15526                    for child in current.named_children(&mut cursor) {
15527                        if child.id() != type_node.id() {
15528                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15529                        }
15530                    }
15531                    work.push(Work::ApplyWrappers(wrappers));
15532                    work.push(Work::Visit(type_node));
15533                }
15534                "pointer_declarator" | "abstract_pointer_declarator" => {
15535                    let child = current
15536                        .child_by_field_name("declarator")
15537                        .or_else(|| current.named_child(0))?;
15538                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15539                    work.push(Work::Visit(child));
15540                }
15541                "reference_declarator" => {
15542                    let child = current
15543                        .child_by_field_name("declarator")
15544                        .or_else(|| current.named_child(0))?;
15545                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15546                    work.push(Work::Visit(child));
15547                }
15548                "array_declarator" | "abstract_array_declarator" => {
15549                    let child = current
15550                        .child_by_field_name("declarator")
15551                        .or_else(|| current.named_child(0))?;
15552                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15553                    work.push(Work::Visit(child));
15554                }
15555                "template_type" => {
15556                    let name_node = current.child_by_field_name("name")?;
15557                    let arguments = current
15558                        .child_by_field_name("arguments")
15559                        .map(|arguments_node| {
15560                            let mut cursor = arguments_node.walk();
15561                            arguments_node
15562                                .named_children(&mut cursor)
15563                                .filter(|child| !child.is_extra() && child.kind() != "comment")
15564                                .collect::<Vec<_>>()
15565                        })
15566                        .unwrap_or_default();
15567                    work.push(Work::BuildGeneric {
15568                        argument_count: arguments.len(),
15569                    });
15570                    work.extend(arguments.into_iter().rev().map(Work::Visit));
15571                    work.push(Work::Visit(name_node));
15572                }
15573                "qualified_identifier"
15574                | "scoped_identifier"
15575                | "scoped_type_identifier"
15576                | "type_identifier"
15577                | "field_identifier"
15578                | "identifier"
15579                | "namespace_identifier"
15580                | "primitive_type" => {
15581                    values.push(builder.named(cpp_structured_named_type(
15582                        current,
15583                        source,
15584                        lexical_scope,
15585                    )?)?);
15586                }
15587                _ => {
15588                    let child = current.child_by_field_name("type").or_else(|| {
15589                        (current.named_child_count() == 1)
15590                            .then(|| current.named_child(0))
15591                            .flatten()
15592                    })?;
15593                    work.push(Work::Visit(child));
15594                }
15595            },
15596            Work::Wrap(wrapper) => {
15597                let root = values.pop()?;
15598                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15599            }
15600            Work::ApplyWrappers(wrappers) => {
15601                let mut root = values.pop()?;
15602                for wrapper in wrappers.into_iter().rev() {
15603                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15604                }
15605                values.push(root);
15606            }
15607            Work::BuildGeneric { argument_count } => {
15608                let value_count = argument_count.checked_add(1)?;
15609                let start = values.len().checked_sub(value_count)?;
15610                let mut built = values.split_off(start);
15611                let base = built.remove(0);
15612                values.push(builder.generic(base, built)?);
15613            }
15614        }
15615    }
15616    (values.len() == 1)
15617        .then(|| values.pop())
15618        .flatten()
15619        .and_then(|root| builder.finish(root))
15620}
15621
15622fn cpp_structured_named_type(
15623    node: Node<'_>,
15624    source: &str,
15625    lexical_scope: &[String],
15626) -> Option<StructuredTypeName> {
15627    let path = cpp_structured_type_path(node, source)?;
15628    let absolute = node.child_by_field_name("scope").is_none()
15629        && node.child(0).is_some_and(|child| child.kind() == "::");
15630    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15631}
15632
15633#[derive(Clone, Copy)]
15634enum CppStructuredTypeWrapper {
15635    Pointer,
15636    LvalueReference,
15637    RvalueReference,
15638    Array,
15639}
15640
15641fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15642    let mut wrappers = Vec::new();
15643    let mut current = node;
15644    loop {
15645        match current.kind() {
15646            "pointer_declarator" | "abstract_pointer_declarator" => {
15647                wrappers.push(CppStructuredTypeWrapper::Pointer)
15648            }
15649            "reference_declarator" | "abstract_reference_declarator" => {
15650                wrappers.push(cpp_reference_wrapper(current)?);
15651            }
15652            "array_declarator" | "abstract_array_declarator" => {
15653                wrappers.push(CppStructuredTypeWrapper::Array)
15654            }
15655            // An initializer and a grouping parenthesis are not type
15656            // structure. Stopping at one of them dropped every wrapper an
15657            // initialized or parenthesized declarator writes, so
15658            // `std::string &alias = value;` reported the unqualified
15659            // `std::string` while `const std::string &value` as a parameter
15660            // reported the reference it shares.
15661            "init_declarator" | "parenthesized_declarator" => {}
15662            _ => break,
15663        }
15664        let Some(child) = current
15665            .child_by_field_name("declarator")
15666            .or_else(|| current.named_child(0))
15667        else {
15668            break;
15669        };
15670        current = child;
15671    }
15672    Some(wrappers)
15673}
15674
15675fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15676    node.children(&mut node.walk())
15677        .find_map(|child| match child.kind() {
15678            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15679            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15680            _ => None,
15681        })
15682}
15683
15684fn cpp_wrap_structured_type(
15685    identity: StructuredTypeIdentity,
15686    wrapper: CppStructuredTypeWrapper,
15687) -> Option<StructuredTypeIdentity> {
15688    match wrapper {
15689        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15690        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15691        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15692        CppStructuredTypeWrapper::Array => identity.wrap_array(),
15693    }
15694}
15695
15696fn cpp_apply_structured_type_wrappers(
15697    mut identity: StructuredTypeIdentity,
15698    wrappers: Vec<CppStructuredTypeWrapper>,
15699) -> Option<StructuredTypeIdentity> {
15700    for wrapper in wrappers.into_iter().rev() {
15701        identity = cpp_wrap_structured_type(identity, wrapper)?;
15702    }
15703    Some(identity)
15704}
15705
15706fn cpp_wrap_structured_type_node(
15707    builder: &mut StructuredTypeIdentityBuilder,
15708    inner: StructuredTypeNodeId,
15709    wrapper: CppStructuredTypeWrapper,
15710) -> Option<StructuredTypeNodeId> {
15711    match wrapper {
15712        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
15713        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
15714        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
15715        CppStructuredTypeWrapper::Array => builder.array(inner),
15716    }
15717}
15718
15719/// The dot-free name components a qualified C++ name node writes, outermost
15720/// first.
15721///
15722/// The walk reads the grammar's `scope` and `name` fields rather than cutting
15723/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
15724/// shape the grammar does not name yields nothing at all.
15725pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
15726    let mut path = Vec::new();
15727    let mut stack = vec![node];
15728    while let Some(current) = stack.pop() {
15729        match current.kind() {
15730            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
15731                let component = node_text(current, source).to_string();
15732                if component.is_empty() {
15733                    return None;
15734                }
15735                path.push(component);
15736            }
15737            "template_type" | "dependent_type" => {
15738                stack.push(current.child_by_field_name("name")?);
15739            }
15740            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15741                stack.push(current.child_by_field_name("name")?);
15742                if let Some(scope) = current.child_by_field_name("scope") {
15743                    stack.push(scope);
15744                }
15745            }
15746            _ => return None,
15747        }
15748    }
15749    (!path.is_empty()).then_some(path)
15750}
15751
15752fn cpp_callable_lexical_scope<'tree>(
15753    node: Node<'tree>,
15754    source: &str,
15755    ancestry: &ParentIndex<'tree>,
15756) -> Vec<String> {
15757    let mut groups = Vec::new();
15758    let mut current = ancestry.parent(node);
15759    while let Some(parent) = current {
15760        if matches!(
15761            parent.kind(),
15762            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
15763        ) && let Some(name_node) = parent.child_by_field_name("name")
15764            && let Some(components) = cpp_structured_type_path(name_node, source)
15765            && !components.is_empty()
15766        {
15767            groups.push(components);
15768        }
15769        current = ancestry.parent(parent);
15770    }
15771    groups.reverse();
15772    groups.into_iter().flatten().collect()
15773}
15774
15775/// Whether a call on this callable's static target can select another body.
15776///
15777/// C++ says this where the member is *declared*, not where it is defined, and
15778/// that is the whole difficulty of the question: virtual-ness is inherited
15779/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
15780/// `Base::run` is), an out-of-line definition repeats none of it, and whether
15781/// a definition's qualifier names a class or a namespace is a class-table
15782/// question. So a row answers `Closed` only when its own node proves it:
15783///
15784/// * a written `final` is the one specifier that ends the family here, and it
15785///   is the only thing that closes a member of a class that has a base clause;
15786/// * a class-body member is `Closed` otherwise only when its class body writes
15787///   no base clause at all, because a base clause is the only evidence in this
15788///   file that some base may declare this member virtual;
15789/// * anything with a virtual-family boundary, a template head, or a
15790///   preprocessor or error region is `Open`;
15791/// * a qualified declaration outside every class body answers `None`: its
15792///   class body, which is where the fact is written, may be in another file.
15793///
15794/// The analyzer joins the `None` rows with the include-visible class-body rows
15795/// of the same logical callable (#3507), the same join #3509 makes for a
15796/// definition's declared access; a row that keeps `None` after that join stays
15797/// unknown and no dispatch proof may be built on it.
15798fn cpp_callable_dispatch_extensibility<'tree>(
15799    function_declarator: Node<'tree>,
15800    ancestry: &ParentIndex<'tree>,
15801) -> Option<DispatchExtensibility> {
15802    let mut declaration = None;
15803    // The first class body this declarator is written in. The upward walk
15804    // visits it anyway, so only the one step from the body to its class node
15805    // is extra, and only for a class-body member.
15806    let mut class_body = None;
15807    let mut current = Some(function_declarator);
15808    while let Some(node) = current {
15809        match node.kind() {
15810            "template_declaration"
15811            | "preproc_if"
15812            | "preproc_ifdef"
15813            | "preproc_else"
15814            | "preproc_elif"
15815            | "preproc_call"
15816            | "ERROR" => return Some(DispatchExtensibility::Open),
15817            "declaration" | "field_declaration" | "function_definition" => {
15818                declaration.get_or_insert(node);
15819            }
15820            "field_declaration_list" => {
15821                class_body.get_or_insert(node);
15822            }
15823            "translation_unit" => break,
15824            _ => {}
15825        }
15826        current = ancestry.parent(node);
15827    }
15828    let Some(declaration) = declaration else {
15829        return Some(DispatchExtensibility::Open);
15830    };
15831
15832    let mut saw_virtual_boundary = false;
15833    let mut stack = vec![declaration];
15834    while let Some(node) = stack.pop() {
15835        match node.kind() {
15836            "compound_statement" | "field_declaration_list" => continue,
15837            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
15838            "virtual"
15839            | "override"
15840            | "virtual_specifier"
15841            | "pure_virtual_clause"
15842            | "template_parameter_list"
15843            | "template_method"
15844            | "template_function"
15845            | "ERROR" => saw_virtual_boundary = true,
15846            _ => {}
15847        }
15848        let mut cursor = node.walk();
15849        stack.extend(node.children(&mut cursor));
15850    }
15851
15852    if saw_virtual_boundary {
15853        return Some(DispatchExtensibility::Open);
15854    }
15855    if let Some(body) = class_body {
15856        // C++ inherits virtual-ness, so a member written without a `virtual`
15857        // keyword is still virtual when a base declares it. Whether some base
15858        // does is not decidable here, so a base clause keeps the answer open.
15859        let has_base_clause = ancestry
15860            .parent(body)
15861            .and_then(cpp_class_like_base_clause)
15862            .is_some();
15863        return Some(if has_base_clause {
15864            DispatchExtensibility::Open
15865        } else {
15866            DispatchExtensibility::Closed
15867        });
15868    }
15869    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
15870        return None;
15871    }
15872    // A namespace or file scope callable is reached by name, never through a
15873    // receiver, so no other body can be selected for a call to it.
15874    Some(DispatchExtensibility::Closed)
15875}
15876
15877fn cpp_callable_linkage<'tree>(
15878    declaration: Node<'tree>,
15879    source: &str,
15880    ancestry: &ParentIndex<'tree>,
15881) -> CallableLinkage {
15882    let mut enclosed_by_class = false;
15883    let mut current = ancestry.parent(declaration);
15884    while let Some(node) = current {
15885        if node.kind() == "namespace_definition"
15886            && node
15887                .child_by_field_name("name")
15888                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
15889        {
15890            return CallableLinkage::Internal;
15891        }
15892        if matches!(
15893            node.kind(),
15894            "class_specifier" | "struct_specifier" | "union_specifier"
15895        ) {
15896            if node
15897                .child_by_field_name("name")
15898                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
15899            {
15900                return CallableLinkage::Internal;
15901            }
15902            enclosed_by_class = true;
15903        }
15904        // An export macro between `class` and the class name can make
15905        // tree-sitter parse the entire class as a function definition. The
15906        // structured recovery proves that this container is a named class
15907        // scope, not a local callable scope.
15908        if node.kind() == "lambda_expression"
15909            || node.kind() == "function_definition"
15910                && !is_recovered_exported_class_container(node, source)
15911        {
15912            return CallableLinkage::Internal;
15913        }
15914        current = ancestry.parent(node);
15915    }
15916
15917    if enclosed_by_class {
15918        return CallableLinkage::External;
15919    }
15920
15921    if cpp_declares_static_storage_class(declaration, source) {
15922        CallableLinkage::Internal
15923    } else {
15924        CallableLinkage::External
15925    }
15926}
15927
15928fn cpp_callable_return_type_text<'tree>(
15929    function_declarator: Node<'tree>,
15930    source: &str,
15931    ancestry: &ParentIndex<'tree>,
15932) -> Option<String> {
15933    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15934        return None;
15935    }
15936    if let Some((return_type, _)) =
15937        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15938    {
15939        let text = normalize_cpp_whitespace(node_text(return_type, source));
15940        return (!text.is_empty()).then_some(text);
15941    }
15942    let mut cursor = function_declarator.walk();
15943    if let Some(trailing) = function_declarator
15944        .named_children(&mut cursor)
15945        .find(|child| child.kind() == "trailing_return_type")
15946        && let Some(type_descriptor) = trailing.named_child(0)
15947    {
15948        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
15949        if !text.is_empty() {
15950            return Some(text);
15951        }
15952    }
15953
15954    let mut current = function_declarator;
15955    let mut indirection = String::new();
15956    while let Some(parent) = ancestry.parent(current) {
15957        if matches!(
15958            parent.kind(),
15959            "function_definition" | "declaration" | "field_declaration"
15960        ) {
15961            let type_node = parent.child_by_field_name("type")?;
15962            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15963                return None;
15964            }
15965            let base = normalize_cpp_whitespace(node_text(type_node, source));
15966            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
15967        }
15968        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15969            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
15970                && parent.named_child_count() == 1
15971                && parent.named_child(0) == Some(current));
15972        if wraps_current_declarator {
15973            match parent.kind() {
15974                "pointer_declarator" => indirection.push('*'),
15975                "reference_declarator" => {
15976                    let reference = parent
15977                        .children(&mut parent.walk())
15978                        .find(|child| !child.is_named())
15979                        .map(|child| node_text(child, source))
15980                        .unwrap_or("&");
15981                    indirection.push_str(reference);
15982                }
15983                "init_declarator" | "parenthesized_declarator" => {}
15984                _ => return None,
15985            }
15986            current = parent;
15987            continue;
15988        }
15989        return None;
15990    }
15991    None
15992}
15993
15994pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
15995    let mut required = 0;
15996    let mut total = 0;
15997    let mut repeated = false;
15998    for slot in cpp_callable_parameter_slots(parameters_node, source) {
15999        match slot {
16000            CppParameterSlot::Declared {
16001                required: is_required,
16002                ..
16003            } => {
16004                total += 1;
16005                if is_required {
16006                    required += 1;
16007                }
16008            }
16009            CppParameterSlot::OptionalVoid => total += 1,
16010            CppParameterSlot::Ellipsis => repeated = true,
16011        }
16012    }
16013    CallableArity::new(required, total, repeated)
16014}
16015
16016/// One entry of a callable's invocation parameter list, plus an optional
16017/// unnamed `void` declaration retained for the legacy arity result.
16018///
16019/// The list excludes an explicit object parameter and a lone `void`. Every
16020/// derivation of parameter arity or type starts from this same sequence, so
16021/// those views cannot disagree about which parameters exist.
16022#[derive(Clone, Copy)]
16023enum CppParameterSlot<'tree> {
16024    Declared {
16025        parameter: Node<'tree>,
16026        required: bool,
16027    },
16028    OptionalVoid,
16029    Ellipsis,
16030}
16031
16032fn cpp_callable_parameter_slots<'tree>(
16033    parameters_node: Node<'tree>,
16034    source: &str,
16035) -> Vec<CppParameterSlot<'tree>> {
16036    let mut slots = Vec::new();
16037    let mut cursor = parameters_node.walk();
16038    for parameter in parameters_node.children(&mut cursor) {
16039        match parameter.kind() {
16040            "parameter_declaration" | "optional_parameter_declaration" => {
16041                let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16042                    && parameter
16043                        .child_by_field_name("type")
16044                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16045                if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16046                    continue;
16047                }
16048                if is_unnamed_void {
16049                    slots.push(CppParameterSlot::OptionalVoid);
16050                    continue;
16051                }
16052                slots.push(CppParameterSlot::Declared {
16053                    parameter,
16054                    required: parameter.kind() == "parameter_declaration",
16055                });
16056            }
16057            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16058                slots.push(CppParameterSlot::Ellipsis);
16059            }
16060            _ => {}
16061        }
16062    }
16063    slots
16064}
16065
16066pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16067    cpp_callable_parameter_slots(parameters_node, source)
16068        .into_iter()
16069        .filter_map(|slot| match slot {
16070            CppParameterSlot::Declared { parameter, .. } => {
16071                Some(cpp_parameter_type(parameter, source))
16072            }
16073            CppParameterSlot::Ellipsis => Some("...".to_string()),
16074            CppParameterSlot::OptionalVoid => None,
16075        })
16076        .collect()
16077}
16078
16079/// Return the written name of each callable parameter in invocation order.
16080/// C and C++ permit declarations without names; those slots deliberately stay
16081/// `None` so consumers can distinguish an unnamed formal from a guessed one.
16082pub(crate) fn cpp_callable_parameter_names(
16083    parameters_node: Node<'_>,
16084    source: &str,
16085) -> Vec<Option<String>> {
16086    cpp_callable_parameter_slots(parameters_node, source)
16087        .into_iter()
16088        .filter_map(|slot| match slot {
16089            CppParameterSlot::Declared { parameter, .. } => Some(
16090                parameter
16091                    .child_by_field_name("declarator")
16092                    .and_then(|declarator| declarator_name_from_node(declarator, source)),
16093            ),
16094            CppParameterSlot::Ellipsis => Some(None),
16095            CppParameterSlot::OptionalVoid => None,
16096        })
16097        .collect()
16098}
16099
16100/// One callable parameter's parser-derived type.
16101///
16102/// A rendered spelling such as `const T&` is a source text, not a type name. A
16103/// consumer that must publish a type into a structured model - a semantic-pack
16104/// type reference, for example - reads this instead.
16105#[derive(Debug, Clone, PartialEq, Eq)]
16106pub enum CppParameterType {
16107    /// The written type reduced to a structured identity. C++ cv-qualifiers
16108    /// have no place in that model and are not represented.
16109    Structured(StructuredTypeIdentity),
16110    /// A `...` pack, which declares no parameter type at all.
16111    Ellipsis,
16112    /// A written type with no structured reduction, such as a macro-obscured,
16113    /// `decltype`-computed, or function-pointer parameter.
16114    Unstructured,
16115}
16116
16117/// The structured type of each invocation parameter, in declaration order.
16118///
16119/// The result is index-parallel with the rendered
16120/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
16121/// callable.
16122pub fn cpp_callable_parameter_type_identities<'tree>(
16123    function_declarator: Node<'tree>,
16124    source: &str,
16125    ancestry: &ParentIndex<'tree>,
16126) -> Vec<CppParameterType> {
16127    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16128        return Vec::new();
16129    };
16130    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16131    cpp_callable_parameter_slots(parameters_node, source)
16132        .into_iter()
16133        .filter_map(|slot| match slot {
16134            CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16135            CppParameterSlot::Declared { parameter, .. } => Some(
16136                cpp_parameter_type_identity(parameter, source, &lexical_scope)
16137                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16138            ),
16139            CppParameterSlot::OptionalVoid => None,
16140        })
16141        .collect()
16142}
16143
16144/// The parser-derived type of one declared object or parameter.
16145///
16146/// The declaration owns the base `type` field while the individual declarator
16147/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
16148/// lets callers distinguish several declarators in one declaration without
16149/// reparsing a rendered signature.
16150pub fn cpp_declaration_type_identity<'tree>(
16151    declaration: Node<'tree>,
16152    declarator: Node<'tree>,
16153    source: &str,
16154    ancestry: &ParentIndex<'tree>,
16155) -> Option<StructuredTypeIdentity> {
16156    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16157    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16158}
16159
16160fn cpp_parameter_type_identity(
16161    parameter: Node<'_>,
16162    source: &str,
16163    lexical_scope: &[String],
16164) -> Option<StructuredTypeIdentity> {
16165    cpp_declaration_type_identity_in_scope(
16166        parameter,
16167        cpp_parameter_declarator(parameter),
16168        source,
16169        lexical_scope,
16170    )
16171}
16172
16173fn cpp_declaration_type_identity_in_scope(
16174    declaration: Node<'_>,
16175    declarator: Option<Node<'_>>,
16176    source: &str,
16177    lexical_scope: &[String],
16178) -> Option<StructuredTypeIdentity> {
16179    let type_node = declaration.child_by_field_name("type")?;
16180    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16181    if let Some(declarator) = declarator {
16182        return cpp_apply_structured_type_wrappers(
16183            identity,
16184            cpp_structured_declarator_wrappers(declarator)?,
16185        );
16186    }
16187    Some(identity)
16188}
16189
16190/// The comparable shape of each invocation parameter, in declaration order.
16191///
16192/// The result is index-parallel with
16193/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
16194/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
16195/// must treat as evidence of nothing rather than as agreement.
16196pub fn cpp_comparable_parameter_shapes<'tree>(
16197    function_declarator: Node<'tree>,
16198    source: &str,
16199    ancestry: &ParentIndex<'tree>,
16200) -> Vec<CppComparableSlot> {
16201    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16202        return Vec::new();
16203    };
16204    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16205    cpp_callable_parameter_slots(parameters_node, source)
16206        .into_iter()
16207        .filter_map(|slot| match slot {
16208            CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16209            CppParameterSlot::Declared { parameter, .. } => Some(
16210                cpp_comparable_parameter(parameter, source, &lexical_scope)
16211                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16212            ),
16213            CppParameterSlot::OptionalVoid => None,
16214        })
16215        .collect()
16216}
16217
16218fn cpp_comparable_parameter(
16219    parameter: Node<'_>,
16220    source: &str,
16221    lexical_scope: &[String],
16222) -> Option<CppComparableParameter> {
16223    let type_node = parameter.child_by_field_name("type")?;
16224    let levels = match cpp_parameter_declarator(parameter) {
16225        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16226        None => Vec::new(),
16227    };
16228    let mut shape = cpp_comparable_type_shape(
16229        type_node,
16230        cpp_cv_qualifiers(parameter, source),
16231        levels,
16232        source,
16233        lexical_scope,
16234    )?;
16235    shape.adjust_parameter_top_level();
16236    Some(shape)
16237}
16238
16239/// The `const` and `volatile` qualifiers written as direct named children of
16240/// `node`.
16241///
16242/// The grammar exposes `type_qualifier` as a non-field named child in exactly
16243/// the three places a parameter's qualifiers can be written: on the
16244/// `parameter_declaration` itself (the base type), on a `type_descriptor`
16245/// (inside a template argument list), and on each `pointer_declarator` level
16246/// (the pointer object). Every other qualifier the grammar admits - `restrict`
16247/// and friends - takes no part in C++ type identity, the same filter
16248/// `cpp_parameter_type` applies to the rendered spelling (#1827).
16249fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16250    let mut qualifiers = CppCvQualifiers::default();
16251    let mut cursor = node.walk();
16252    for child in node.named_children(&mut cursor) {
16253        if child.kind() != "type_qualifier" {
16254            continue;
16255        }
16256        match node_text(child, source) {
16257            "const" => qualifiers.konst = true,
16258            "volatile" => qualifiers.volatil = true,
16259            _ => {}
16260        }
16261    }
16262    qualifiers
16263}
16264
16265#[derive(Clone, Copy, Default)]
16266struct CppCvQualifiers {
16267    konst: bool,
16268    volatil: bool,
16269}
16270
16271impl CppCvQualifiers {
16272    fn union(self, other: Self) -> Self {
16273        Self {
16274            konst: self.konst || other.konst,
16275            volatil: self.volatil || other.volatil,
16276        }
16277    }
16278}
16279
16280/// One pointer, reference or array level a declarator chain adds.
16281#[derive(Clone, Copy)]
16282enum CppComparableLevel {
16283    Pointer { konst: bool, volatil: bool },
16284    Reference,
16285    Array,
16286}
16287
16288/// The levels `declarator` adds, outermost written level first.
16289///
16290/// C++ declarator syntax binds inside out: the level written closest to the
16291/// declared name is the outermost type constructor, and tree-sitter nests it
16292/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
16293/// applies in order to reach "array of pointer to int", and the qualifier of
16294/// `int * const *p` is read on the level it was written next to, the inner
16295/// pointer of the resulting type.
16296///
16297/// A declarator chain that names a function type - a function-pointer
16298/// parameter - has no comparable shape and reports `None`, matching the
16299/// structured identity channel.
16300fn cpp_comparable_declarator_levels(
16301    declarator: Node<'_>,
16302    source: &str,
16303) -> Option<Vec<CppComparableLevel>> {
16304    let mut levels = Vec::new();
16305    let mut current = declarator;
16306    loop {
16307        match current.kind() {
16308            "pointer_declarator" | "abstract_pointer_declarator" => {
16309                let qualifiers = cpp_cv_qualifiers(current, source);
16310                levels.push(CppComparableLevel::Pointer {
16311                    konst: qualifiers.konst,
16312                    volatil: qualifiers.volatil,
16313                });
16314            }
16315            "reference_declarator" | "abstract_reference_declarator" => {
16316                levels.push(CppComparableLevel::Reference);
16317            }
16318            "array_declarator" | "abstract_array_declarator" => {
16319                levels.push(CppComparableLevel::Array);
16320            }
16321            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16322            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16323            _ => return None,
16324        }
16325        let Some(next) = cpp_nested_declarator(current) else {
16326            return Some(levels);
16327        };
16328        current = next;
16329    }
16330}
16331
16332/// Reduce one written type to a comparable arena.
16333///
16334/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
16335/// two additions: each visited type node carries the cv-qualifiers written on
16336/// it, and declarator levels arrive as a prepared list rather than being
16337/// rediscovered inside the walk.
16338fn cpp_comparable_type_shape(
16339    type_node: Node<'_>,
16340    qualifiers: CppCvQualifiers,
16341    levels: Vec<CppComparableLevel>,
16342    source: &str,
16343    lexical_scope: &[String],
16344) -> Option<CppComparableParameter> {
16345    enum Work<'tree> {
16346        Visit {
16347            node: Node<'tree>,
16348            qualifiers: CppCvQualifiers,
16349        },
16350        ApplyLevels(Vec<CppComparableLevel>),
16351        BuildGeneric {
16352            argument_count: usize,
16353        },
16354    }
16355
16356    let mut nodes: Vec<CppComparableNode> = Vec::new();
16357    let mut values: Vec<usize> = Vec::new();
16358    let mut work = vec![
16359        Work::ApplyLevels(levels),
16360        Work::Visit {
16361            node: type_node,
16362            qualifiers,
16363        },
16364    ];
16365    while let Some(next) = work.pop() {
16366        match next {
16367            Work::Visit { node, qualifiers } => match node.kind() {
16368                "type_descriptor" => {
16369                    let inner_type = node
16370                        .child_by_field_name("type")
16371                        .or_else(|| node.named_child(0))?;
16372                    let mut cursor = node.walk();
16373                    let declarator = node.child_by_field_name("declarator").or_else(|| {
16374                        node.named_children(&mut cursor).find(|child| {
16375                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
16376                        })
16377                    });
16378                    let levels = match declarator {
16379                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16380                        None => Vec::new(),
16381                    };
16382                    work.push(Work::ApplyLevels(levels));
16383                    work.push(Work::Visit {
16384                        node: inner_type,
16385                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16386                    });
16387                }
16388                "sized_type_specifier" => {
16389                    // `unsigned char` is one primitive type whose components are
16390                    // partly unnamed tokens, so the whole specifier is its own
16391                    // name component. Reducing it to the `type` child would make
16392                    // `f(unsigned char)` and `f(char)` compare equal.
16393                    let name = StructuredTypeName::new(
16394                        vec![normalize_cpp_whitespace(node_text(node, source))],
16395                        lexical_scope.to_vec(),
16396                        false,
16397                    )?;
16398                    values.push(cpp_push_comparable_node(
16399                        &mut nodes,
16400                        CppComparableNode::Named {
16401                            name,
16402                            primitive: true,
16403                            konst: qualifiers.konst,
16404                            volatil: qualifiers.volatil,
16405                        },
16406                    ));
16407                }
16408                "qualified_identifier"
16409                | "scoped_identifier"
16410                | "scoped_type_identifier"
16411                | "type_identifier"
16412                | "field_identifier"
16413                | "identifier"
16414                | "namespace_identifier"
16415                | "primitive_type"
16416                | "template_type" => {
16417                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
16418                    values.push(cpp_push_comparable_node(
16419                        &mut nodes,
16420                        CppComparableNode::Named {
16421                            name,
16422                            primitive: node.kind() == "primitive_type",
16423                            konst: qualifiers.konst,
16424                            volatil: qualifiers.volatil,
16425                        },
16426                    ));
16427                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16428                        let mut cursor = arguments_node.walk();
16429                        let arguments = arguments_node
16430                            .named_children(&mut cursor)
16431                            .filter(|child| !child.is_extra() && child.kind() != "comment")
16432                            .collect::<Vec<_>>();
16433                        work.push(Work::BuildGeneric {
16434                            argument_count: arguments.len(),
16435                        });
16436                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16437                            node: argument,
16438                            qualifiers: CppCvQualifiers::default(),
16439                        }));
16440                    }
16441                }
16442                _ => {
16443                    let inner = node.child_by_field_name("type").or_else(|| {
16444                        (node.named_child_count() == 1)
16445                            .then(|| node.named_child(0))
16446                            .flatten()
16447                    })?;
16448                    work.push(Work::Visit {
16449                        node: inner,
16450                        qualifiers,
16451                    });
16452                }
16453            },
16454            Work::ApplyLevels(levels) => {
16455                let mut root = values.pop()?;
16456                for level in levels {
16457                    let node = match level {
16458                        CppComparableLevel::Pointer { konst, volatil } => {
16459                            CppComparableNode::Pointer {
16460                                inner: root,
16461                                konst,
16462                                volatil,
16463                            }
16464                        }
16465                        CppComparableLevel::Reference => {
16466                            CppComparableNode::Reference { inner: root }
16467                        }
16468                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16469                    };
16470                    root = cpp_push_comparable_node(&mut nodes, node);
16471                }
16472                values.push(root);
16473            }
16474            Work::BuildGeneric { argument_count } => {
16475                let value_count = argument_count.checked_add(1)?;
16476                let start = values.len().checked_sub(value_count)?;
16477                let mut built = values.split_off(start);
16478                let base = built.remove(0);
16479                values.push(cpp_push_comparable_node(
16480                    &mut nodes,
16481                    CppComparableNode::Generic {
16482                        base,
16483                        arguments: built,
16484                    },
16485                ));
16486            }
16487        }
16488    }
16489    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16490    debug_assert_eq!(
16491        root,
16492        nodes.len().saturating_sub(1),
16493        "comparable nodes are appended in post-order, so the root is the last one"
16494    );
16495    Some(CppComparableParameter::new(nodes, root))
16496}
16497
16498fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16499    nodes.push(node);
16500    nodes.len() - 1
16501}
16502
16503/// The template argument list of the name `node` terminates in, if any.
16504///
16505/// `std::vector<int>` writes its arguments on the `name` of a qualified
16506/// identifier, so a walk that stopped at the qualified node would reduce
16507/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
16508fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16509    let mut current = node;
16510    loop {
16511        match current.kind() {
16512            "template_type" => return current.child_by_field_name("arguments"),
16513            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16514                current = current.child_by_field_name("name")?;
16515            }
16516            _ => return None,
16517        }
16518    }
16519}
16520
16521/// The callable declarator of the declaration that covers `start_byte`.
16522///
16523/// A consumer that holds a declaration's recorded byte position rather than its
16524/// syntax node - external header extraction, for instance - uses this to reach
16525/// the same `function_declarator` the declaration walk read.
16526pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16527    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16528    loop {
16529        if matches!(
16530            current.kind(),
16531            "declaration" | "field_declaration" | "function_definition"
16532        ) && let Some(declarator) = current
16533            .child_by_field_name("declarator")
16534            .and_then(extract_function_declarator)
16535        {
16536            return Some(declarator);
16537        }
16538        current = current.parent()?;
16539    }
16540}
16541
16542fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16543    let mut labels = Vec::new();
16544    let mut cursor = parameters_node.walk();
16545    for child in parameters_node.children(&mut cursor) {
16546        match child.kind() {
16547            "parameter_declaration" | "optional_parameter_declaration" => {
16548                if let Some(name_node) = child
16549                    .child_by_field_name("declarator")
16550                    .and_then(cpp_declarator_label_node)
16551                {
16552                    labels.push(name_node);
16553                } else {
16554                    labels.push(child);
16555                }
16556            }
16557            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16558                labels.push(child);
16559            }
16560            _ => {}
16561        }
16562    }
16563    labels
16564}
16565
16566fn cpp_signature_search_start<'tree>(
16567    signature: &str,
16568    function_declarator: Node<'tree>,
16569    source: &str,
16570    ancestry: &ParentIndex<'tree>,
16571) -> usize {
16572    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16573        return 0;
16574    };
16575    let raw = node_text(enclosing, source);
16576    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16577    let offset = function_declarator
16578        .start_byte()
16579        .saturating_sub(enclosing.start_byte())
16580        .saturating_sub(leading_trim_bytes);
16581    offset.min(signature.len())
16582}
16583
16584fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16585    match node.kind() {
16586        "identifier" | "field_identifier" => Some(node),
16587        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16588            .child_by_field_name("declarator")
16589            .or_else(|| last_named_child(node))
16590            .and_then(cpp_declarator_label_node),
16591        "array_declarator" => node
16592            .child_by_field_name("declarator")
16593            .and_then(cpp_declarator_label_node),
16594        "function_declarator" => node
16595            .child_by_field_name("declarator")
16596            .or_else(|| node.child_by_field_name("name"))
16597            .or_else(|| last_named_child(node))
16598            .and_then(cpp_declarator_label_node),
16599        _ => None,
16600    }
16601}
16602
16603fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16604    let base_type = parameter
16605        .child_by_field_name("type")
16606        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16607        .unwrap_or_default();
16608    let declarator = cpp_parameter_declarator(parameter);
16609    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
16610    // are discarded, so `f(const int)` and `f(int)` declare one function. A
16611    // qualifier written next to the parameter's type is only top-level when
16612    // the declarator adds no indirection; behind a pointer, reference or array
16613    // declarator the same qualifier belongs to the pointee, referent or
16614    // element and keeps distinguishing the type (#1827).
16615    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16616    let mut cursor = parameter.walk();
16617    let qualifiers = parameter
16618        .named_children(&mut cursor)
16619        .filter(|child| child.kind() == "type_qualifier")
16620        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16621        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16622        .collect::<Vec<_>>()
16623        .join(" ");
16624    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16625        (true, _) => base_type,
16626        (_, true) => qualifiers,
16627        (false, false) => format!("{qualifiers} {base_type}"),
16628    };
16629    let declarator_suffix = declarator
16630        .map(|node| cpp_declarator_suffix_without_name(node, source))
16631        .unwrap_or_default();
16632
16633    let combined = if type_text.is_empty() {
16634        declarator_suffix
16635    } else if declarator_suffix.is_empty() {
16636        type_text
16637    } else {
16638        format!("{type_text} {declarator_suffix}")
16639    };
16640    normalize_cpp_type_text(&combined)
16641}
16642
16643fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16644    parameter.child_by_field_name("declarator").or_else(|| {
16645        // Some unnamed prototype parameters expose their abstract declarator
16646        // as a direct named child without the grammar's `declarator` field.
16647        // Recover only the structured abstract-declarator node; the parameter's
16648        // type and qualifiers are distinct children and must not be guessed from
16649        // source text.
16650        cpp_first_abstract_declarator_child(parameter)
16651    })
16652}
16653
16654/// Whether a parameter's declarator chain adds indirection - a pointer,
16655/// reference, array or function declarator - to the parameter's written type.
16656pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16657    let mut current = Some(declarator);
16658    while let Some(node) = current {
16659        if matches!(
16660            node.kind(),
16661            "pointer_declarator"
16662                | "abstract_pointer_declarator"
16663                | "reference_declarator"
16664                | "abstract_reference_declarator"
16665                | "array_declarator"
16666                | "abstract_array_declarator"
16667                | "function_declarator"
16668                | "abstract_function_declarator"
16669        ) {
16670            return true;
16671        }
16672        current = cpp_nested_declarator(node);
16673    }
16674    false
16675}
16676
16677fn is_cpp_abstract_declarator(kind: &str) -> bool {
16678    matches!(
16679        kind,
16680        "abstract_pointer_declarator"
16681            | "abstract_reference_declarator"
16682            | "abstract_array_declarator"
16683            | "abstract_function_declarator"
16684            | "abstract_parenthesized_declarator"
16685    )
16686}
16687
16688fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16689    let mut cursor = node.walk();
16690    node.named_children(&mut cursor)
16691        .find(|child| is_cpp_abstract_declarator(child.kind()))
16692}
16693
16694fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16695    node.child_by_field_name("declarator").or_else(|| {
16696        if is_cpp_abstract_declarator(node.kind()) {
16697            cpp_first_abstract_declarator_child(node)
16698        } else {
16699            // Named declarators historically use their last named child when
16700            // tree-sitter omits the field. Keep that broad fallback for
16701            // attributed, variadic, and recovered named shapes.
16702            last_named_child(node)
16703        }
16704    })
16705}
16706
16707fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
16708    match node.kind() {
16709        "identifier" | "field_identifier" => String::new(),
16710        "pointer_declarator" | "abstract_pointer_declarator" => {
16711            let inner = cpp_nested_declarator(node)
16712                .map(|child| cpp_declarator_suffix_without_name(child, source))
16713                .unwrap_or_default();
16714            format!("*{inner}")
16715        }
16716        "reference_declarator" | "abstract_reference_declarator" => {
16717            let inner = cpp_nested_declarator(node)
16718                .map(|child| cpp_declarator_suffix_without_name(child, source))
16719                .unwrap_or_default();
16720            let reference = node
16721                .children(&mut node.walk())
16722                .find(|child| matches!(child.kind(), "&" | "&&"))
16723                .map(|child| node_text(child, source))
16724                .unwrap_or("&");
16725            format!("{reference}{inner}")
16726        }
16727        "array_declarator" | "abstract_array_declarator" => {
16728            let inner = cpp_nested_declarator(node)
16729                .map(|child| cpp_declarator_suffix_without_name(child, source))
16730                .unwrap_or_default();
16731            let size = node
16732                .child_by_field_name("size")
16733                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16734                .unwrap_or_default();
16735            format!("{inner}[{size}]")
16736        }
16737        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
16738            let inner = cpp_nested_declarator(node);
16739            inner
16740                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
16741                .unwrap_or_default()
16742        }
16743        "function_declarator" | "abstract_function_declarator" => {
16744            let inner = cpp_nested_declarator(node)
16745                .map(|child| cpp_declarator_suffix_without_name(child, source))
16746                .unwrap_or_default();
16747            let params = node
16748                .child_by_field_name("parameters")
16749                .map(|child| cpp_parameter_signature(child, source))
16750                .unwrap_or_else(|| "()".to_string());
16751            format!("{inner}{params}")
16752        }
16753        _ => {
16754            let text = normalize_cpp_whitespace(node_text(node, source));
16755            let name = extract_declarator_name(node, source);
16756            if name.is_empty() {
16757                text
16758            } else {
16759                text.replace(&name, "").trim().to_string()
16760            }
16761        }
16762    }
16763}
16764
16765fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
16766    collapse_cpp_whitespace(
16767        suffix
16768            .trim()
16769            .trim_start_matches("->")
16770            .trim_start_matches('{')
16771            .trim_end_matches(';'),
16772    )
16773}
16774
16775pub fn normalize_cpp_whitespace(value: &str) -> String {
16776    collapse_cpp_whitespace(value)
16777}
16778
16779fn normalize_cpp_type_text(value: &str) -> String {
16780    collapse_cpp_whitespace(value)
16781        .replace(", ", ",")
16782        .replace(" <", "<")
16783        .replace("< ", "<")
16784        .replace(" >", ">")
16785}
16786
16787fn collapse_cpp_whitespace(value: &str) -> String {
16788    let mut result = String::new();
16789    let mut prev_space = false;
16790    for ch in value.chars() {
16791        if ch.is_whitespace() {
16792            if !prev_space {
16793                result.push(' ');
16794            }
16795            prev_space = true;
16796        } else {
16797            result.push(ch);
16798            prev_space = false;
16799        }
16800    }
16801    result.trim().to_string()
16802}
16803
16804fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
16805    child_by_field_or_named_child_of_kinds(
16806        node,
16807        "body",
16808        &[
16809            "declaration_list",
16810            "field_declaration_list",
16811            "enumerator_list",
16812        ],
16813    )
16814}
16815
16816/// Return a class body's actual closing brace when the parser supplied one.
16817///
16818/// A malformed namespace sentinel can leave a class node carrying unrelated
16819/// parser errors even though its own class body is complete.  `has_error()` is
16820/// therefore too coarse an admission predicate for sentinel ownership.  The
16821/// body list, however, exposes the opening and closing punctuation directly;
16822/// a real (non-missing) final `}` proves that the class did not borrow the
16823/// enclosing namespace's close.  Requiring the body to end before its parent
16824/// container also rejects a recovered node whose body swallowed that outer
16825/// boundary.
16826fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
16827    if !matches!(
16828        node.kind(),
16829        "class_specifier" | "struct_specifier" | "union_specifier"
16830    ) {
16831        return None;
16832    }
16833    let body = cpp_body_node(node)?;
16834    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
16835        return None;
16836    }
16837    let open = body.child(0)?;
16838    let close = body.child(body.child_count().checked_sub(1)?)?;
16839    if open.kind() != "{"
16840        || open.is_missing()
16841        || close.kind() != "}"
16842        || close.is_missing()
16843        || close.end_byte() != body.end_byte()
16844        || body.end_byte() > node.end_byte()
16845        || node
16846            .parent()
16847            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
16848    {
16849        return None;
16850    }
16851    Some(close)
16852}
16853
16854fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
16855    if node.kind() == "namespace_definition" {
16856        return true;
16857    }
16858    let mut cursor = node.walk();
16859    node.named_children(&mut cursor)
16860        .any(cpp_contains_namespace_definition)
16861}
16862
16863struct CppNestedNamespaceSentinel<'tree> {
16864    function: Node<'tree>,
16865    body: Node<'tree>,
16866    namespace_components: Vec<String>,
16867}
16868
16869/// Owned structural recovery metadata for a namespace-sentinel region.
16870///
16871/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
16872/// instead of the namespace/class scopes that the declaration visitor restores.
16873/// The inverted usage walk has the original CST, so it needs the same ownership
16874/// evidence without borrowing parser nodes across its file scan.  Keep this
16875/// descriptor deliberately source-range based: callers can match a reference
16876/// node by containment and then resolve its structured type spelling in the
16877/// recovered class scope.
16878#[derive(Debug, Clone)]
16879pub struct CppSentinelRecoveredOwner {
16880    pub range: Range,
16881    /// Start of the qualified owner name (`btree<P>::method`).  A leading
16882    /// return type before this byte is looked up from the namespace; parameters,
16883    /// trailing returns, and the body use the member owner scope.
16884    pub owner_name_start_byte: usize,
16885    /// Number of leading components belonging to the namespace rather than
16886    /// the qualified class owner.  A leading return type is looked up before
16887    /// every owner component, not merely before the innermost class.
16888    pub namespace_component_count: usize,
16889    pub scope_components: Vec<String>,
16890}
16891
16892#[derive(Debug, Clone)]
16893pub struct CppSentinelRecoveredClass {
16894    pub namespace_range: Range,
16895    pub namespace_scope_components: Vec<String>,
16896    pub class_range: Range,
16897    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
16898    pub scope_components: Vec<String>,
16899    /// Qualified out-of-line member definitions owned by this class.  Their
16900    /// ranges may extend beyond `class_range` when the malformed sentinel
16901    /// swallowed the namespace close and left definitions as function siblings.
16902    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
16903}
16904
16905/// Resolve the lexical scope restored for a node in a malformed
16906/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
16907/// outrank class spans, which in turn outrank the surviving namespace body.
16908/// The class ancestor suffix is recovered from the original CST so nested
16909/// members keep their complete `Outer::Inner` owner chain.
16910pub fn cpp_sentinel_recovered_scope_for_node(
16911    node: Node<'_>,
16912    source: &str,
16913    recovered_classes: &[CppSentinelRecoveredClass],
16914) -> Option<Vec<String>> {
16915    let contains =
16916        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
16917    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
16918    for recovered in recovered_classes {
16919        for owner in recovered
16920            .owner_ranges
16921            .iter()
16922            .filter(|owner| contains(owner.range))
16923        {
16924            let replace = best_owner.is_none_or(|existing| {
16925                owner.range.end_byte.saturating_sub(owner.range.start_byte)
16926                    < existing
16927                        .range
16928                        .end_byte
16929                        .saturating_sub(existing.range.start_byte)
16930            });
16931            if replace {
16932                best_owner = Some(owner);
16933            }
16934        }
16935    }
16936    if let Some(owner) = best_owner {
16937        let mut scope = owner.scope_components.clone();
16938        if node.start_byte() < owner.owner_name_start_byte {
16939            scope.truncate(owner.namespace_component_count);
16940        }
16941        return Some(scope);
16942    }
16943
16944    let class = recovered_classes
16945        .iter()
16946        .filter(|recovered| contains(recovered.class_range))
16947        .min_by_key(|recovered| {
16948            recovered
16949                .class_range
16950                .end_byte
16951                .saturating_sub(recovered.class_range.start_byte)
16952        });
16953    let class_scope = class.is_some();
16954    let mut scope = if let Some(class) = class {
16955        class.scope_components.clone()
16956    } else {
16957        let namespace = recovered_classes
16958            .iter()
16959            .filter(|recovered| contains(recovered.namespace_range))
16960            .min_by_key(|recovered| {
16961                recovered
16962                    .namespace_range
16963                    .end_byte
16964                    .saturating_sub(recovered.namespace_range.start_byte)
16965            })?;
16966        let mut scope = namespace.namespace_scope_components.clone();
16967        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
16968        let common_prefix = scope
16969            .iter()
16970            .zip(&parser_namespace)
16971            .take_while(|(recovered, parser)| recovered == parser)
16972            .count();
16973        scope.extend(parser_namespace.into_iter().skip(common_prefix));
16974        scope
16975    };
16976    if class_scope {
16977        let mut ancestor_components = Vec::new();
16978        let mut ancestor = node.parent();
16979        while let Some(current) = ancestor {
16980            if matches!(
16981                current.kind(),
16982                "class_specifier" | "struct_specifier" | "union_specifier"
16983            ) && let Some(name) = current.child_by_field_name("name")
16984                && let Some(name_components) = cpp_name_components(name, source)
16985            {
16986                ancestor_components.push(
16987                    name_components
16988                        .into_iter()
16989                        .map(|component| component.name)
16990                        .collect::<Vec<_>>(),
16991                );
16992            }
16993            ancestor = current.parent();
16994        }
16995        ancestor_components.reverse();
16996        let base_len = scope.len();
16997        for component in ancestor_components.into_iter().flatten() {
16998            if scope.len() >= base_len && scope.last() == Some(&component) {
16999                continue;
17000            }
17001            scope.push(component);
17002        }
17003    }
17004    Some(scope)
17005}
17006
17007struct CppSentinelFragmentedClassTail<'tree> {
17008    class_node: Node<'tree>,
17009    template_node: Option<Node<'tree>>,
17010    name: String,
17011    raw_supertypes: Option<Vec<String>>,
17012    fragmented: FragmentedExportBody,
17013    consumed_start: usize,
17014}
17015
17016struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17017    name: String,
17018    open: Node<'tree>,
17019    raw_supertypes: Option<Vec<String>>,
17020}
17021
17022struct CppSentinelDirectBodyClassRegion {
17023    namespace_components: Vec<String>,
17024    class_start: usize,
17025    class_start_line: usize,
17026    class_close_end: usize,
17027    class_close_line: usize,
17028    name: String,
17029}
17030
17031fn cpp_sentinel_body_class_candidate<'tree>(
17032    child: Node<'tree>,
17033) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17034    if matches!(
17035        child.kind(),
17036        "class_specifier" | "struct_specifier" | "union_specifier"
17037    ) {
17038        return Some((child, None));
17039    }
17040    if child.kind() != "template_declaration" {
17041        if child.kind() == "declaration" {
17042            return Some((first_class_like_child(child)?, None));
17043        }
17044        return None;
17045    }
17046    let mut cursor = child.walk();
17047    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17048        if matches!(
17049            candidate.kind(),
17050            "class_specifier" | "struct_specifier" | "union_specifier"
17051        ) {
17052            Some(candidate)
17053        } else if candidate.kind() == "declaration" {
17054            first_class_like_child(candidate)
17055        } else {
17056            None
17057        }
17058    })?;
17059    Some((class_node, Some(child)))
17060}
17061
17062/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
17063/// namespace-sentinel body when a later member macro ends the bogus sentinel
17064/// function before the real class close. The anonymous class/open tokens and
17065/// direct identifier are the structural proof; a retained direct close would
17066/// be an ordinary malformed class rather than the fragmented tail handled here.
17067fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17068    node: Node<'tree>,
17069    source: &str,
17070) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17071    let name = malformed_class_error_owner_name(node, source)?;
17072    let mut cursor = node.walk();
17073    let children = node.children(&mut cursor).collect::<Vec<_>>();
17074    let keyword = children.first()?;
17075    let open_index = children.iter().position(|child| child.kind() == "{")?;
17076    if children[open_index + 1..]
17077        .iter()
17078        .any(|child| child.kind() == "}")
17079    {
17080        return None;
17081    }
17082    let raw_supertypes =
17083        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17084    Some(CppSentinelFragmentedClassErrorPrefix {
17085        name,
17086        open: children[open_index],
17087        raw_supertypes,
17088    })
17089}
17090
17091fn cpp_sentinel_direct_body_class_candidate<'tree>(
17092    child: Node<'tree>,
17093) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17094    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17095        return Some(candidate);
17096    }
17097    if child.kind() != "template_declaration" {
17098        return None;
17099    }
17100    let mut cursor = child.walk();
17101    let wrapper = child
17102        .named_children(&mut cursor)
17103        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17104    Some((first_class_like_child(wrapper)?, Some(child)))
17105}
17106
17107fn cpp_sentinel_direct_namespace_components(
17108    function: Node<'_>,
17109    body: Node<'_>,
17110    source: &str,
17111) -> Option<Vec<String>> {
17112    let mut cursor = function.walk();
17113    let children = function
17114        .named_children(&mut cursor)
17115        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17116        .collect::<Vec<_>>();
17117    let sentinel_index = children.iter().rposition(|child| {
17118        direct_identifier_name(*child, source)
17119            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17120    })?;
17121    let mut identifiers = Vec::new();
17122    let mut stack = children[sentinel_index + 1..]
17123        .iter()
17124        .rev()
17125        .copied()
17126        .collect::<Vec<_>>();
17127    while let Some(current) = stack.pop() {
17128        if let Some(name) = direct_identifier_name(current, source) {
17129            identifiers.push(name);
17130            continue;
17131        }
17132        let mut cursor = current.walk();
17133        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17134        stack.extend(children.into_iter().rev());
17135    }
17136    let [keyword, namespace] = identifiers.as_slice() else {
17137        return None;
17138    };
17139    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17140        .then(|| vec![namespace.clone()])
17141}
17142
17143fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17144    let mut sibling = class_semicolon.next_named_sibling();
17145    let namespace_close = loop {
17146        let Some(current) = sibling else {
17147            return false;
17148        };
17149        sibling = current.next_named_sibling();
17150        if current.kind() != "comment" {
17151            break current;
17152        }
17153    };
17154    if !cpp_is_stray_close_brace(namespace_close, source) {
17155        return false;
17156    }
17157    loop {
17158        let Some(current) = sibling else {
17159            return false;
17160        };
17161        sibling = current.next_named_sibling();
17162        if current.kind() == "comment" {
17163            continue;
17164        }
17165        return direct_identifier_name(current, source)
17166            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17167    }
17168}
17169
17170fn cpp_sentinel_macro_body_class_region<'tree>(
17171    node: Node<'tree>,
17172    source: &str,
17173    ancestry: &ParentIndex<'tree>,
17174) -> Option<CppSentinelDirectBodyClassRegion> {
17175    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17176        return None;
17177    };
17178    if node.kind() != "function_definition" || !node.has_error() {
17179        return None;
17180    }
17181    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17182    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17183    let mut cursor = body.walk();
17184    let candidates = body
17185        .named_children(&mut cursor)
17186        .filter_map(cpp_sentinel_direct_body_class_candidate)
17187        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17188        .collect::<Vec<_>>();
17189    let [(class_node, template_node)] = candidates.as_slice() else {
17190        return None;
17191    };
17192    let original_body = cpp_body_node(*class_node)?;
17193    let name = class_like_name(*class_node, source, ancestry)?;
17194    if name.is_empty() || cpp_export_macro_token(&name) {
17195        return None;
17196    }
17197
17198    let mut sibling = node.next_named_sibling();
17199    let (class_close_start, class_close_end, class_close_line) = loop {
17200        let current = sibling?;
17201        let next = current.next_named_sibling();
17202        if cpp_is_stray_close_brace(current, source)
17203            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17204        {
17205            let semicolon = next.expect("checked above");
17206            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17207                return None;
17208            }
17209            break (
17210                current.start_byte(),
17211                semicolon.end_byte(),
17212                semicolon.end_position().row + 1,
17213            );
17214        }
17215        sibling = next;
17216    };
17217    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17218    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17219    let root = tree.root_node();
17220    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17221    // The region reparse is its own tree, so it needs its own parent index;
17222    // the caller's index answers nothing about these nodes.
17223    let reparsed_ancestry = ParentIndex::new(root);
17224    let reparsed =
17225        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17226    if reparsed.name != name
17227        || reparsed.declaration_node.start_byte() != class_node.start_byte()
17228        || reparsed.body.start_byte() != original_body.start_byte()
17229        || class_close_start <= reparsed.body.end_byte()
17230        || class_close_end <= class_node.end_byte()
17231    {
17232        return None;
17233    }
17234    Some(CppSentinelDirectBodyClassRegion {
17235        namespace_components,
17236        class_start: reparse_start,
17237        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17238            node.start_position().row + 1
17239        }),
17240        class_close_end,
17241        class_close_line,
17242        name,
17243    })
17244}
17245
17246/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
17247/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
17248///
17249/// The parser puts the namespace opener and the malformed function in one root
17250/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
17251/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
17252/// the malformed function must begin with an all-caps type, then an ERROR whose
17253/// sole identifier is `namespace`, followed by the inner namespace identifier
17254/// and a compound body; and that body must contain a complete named class or a
17255/// structurally fragmented class prefix. A text reparse cannot prove any of
17256/// those ownership boundaries.
17257fn cpp_nested_namespace_sentinel<'tree>(
17258    node: Node<'tree>,
17259    source: &str,
17260    ancestry: &ParentIndex<'tree>,
17261) -> Option<CppNestedNamespaceSentinel<'tree>> {
17262    // The shape checks below are the admission proof: a `type_identifier`
17263    // sentinel followed by an `ERROR` holding the `namespace` keyword implies
17264    // parser damage on its own, and under the macro-declaration grammar the
17265    // `macro_modifier` wrapper and the keyword in the type field are each
17266    // admitted only where the parse would otherwise contain an error.
17267    let (function, mut namespace_components) = if node.kind() == "ERROR" {
17268        let mut cursor = node.walk();
17269        let functions = node
17270            .named_children(&mut cursor)
17271            .filter(|child| child.kind() == "function_definition")
17272            .collect::<Vec<_>>();
17273        let [function] = functions.as_slice() else {
17274            return None;
17275        };
17276        let mut cursor = node.walk();
17277        let children = node.children(&mut cursor).collect::<Vec<_>>();
17278        let function_index = children
17279            .iter()
17280            .position(|child| same_node(*child, *function))?;
17281        let [outer_keyword, outer_name, outer_open] =
17282            children.get(function_index.checked_sub(3)?..function_index)?
17283        else {
17284            return None;
17285        };
17286        if outer_keyword.kind() != "namespace"
17287            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17288            || outer_open.kind() != "{"
17289        {
17290            return None;
17291        }
17292        (
17293            *function,
17294            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17295        )
17296    } else if node.kind() == "function_definition" {
17297        let declaration_list = node.parent()?;
17298        let namespace = declaration_list.parent()?;
17299        if declaration_list.kind() != "declaration_list"
17300            || namespace.kind() != "namespace_definition"
17301            || namespace.child_by_field_name("body") != Some(declaration_list)
17302        {
17303            return None;
17304        }
17305        (node, Vec::new())
17306    } else {
17307        return None;
17308    };
17309
17310    let mut cursor = function.walk();
17311    let named = function
17312        .named_children(&mut cursor)
17313        .filter(|child| child.kind() != "comment")
17314        .collect::<Vec<_>>();
17315    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17316        return None;
17317    };
17318    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
17319    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
17320    // the same token is wrapped in.
17321    let sentinel = if first_type.kind() == "type_identifier" {
17322        normalize_cpp_whitespace(node_text(*first_type, source))
17323    } else {
17324        let identifier = macro_modifier_identifier(*first_type)?;
17325        normalize_cpp_whitespace(node_text(identifier, source))
17326    };
17327    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17328        return None;
17329    }
17330    // The mis-lexed `namespace` keyword: an `ERROR` holding it under the older
17331    // grammar, or the function's bogus type field under the macro-declaration
17332    // grammar, where a keyword cannot be a type.
17333    let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17334        inner_error.named_child(0)?
17335    } else if inner_error.kind() == "type_identifier" {
17336        *inner_error
17337    } else {
17338        return None;
17339    };
17340    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17341        return None;
17342    }
17343    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17344        return None;
17345    }
17346    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17347    if inner_name.is_empty() || body.kind() != "compound_statement" {
17348        return None;
17349    }
17350    namespace_components.push(inner_name);
17351
17352    let mut cursor = body.walk();
17353    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17354        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17355            cpp_body_node(class_node).is_some()
17356                && class_like_name(class_node, source, ancestry)
17357                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
17358        })
17359    });
17360    if !has_complete_class
17361        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17362    {
17363        return None;
17364    }
17365
17366    Some(CppNestedNamespaceSentinel {
17367        function,
17368        body: *body,
17369        namespace_components,
17370    })
17371}
17372
17373/// Recognize a namespace-begin sentinel directly beneath the translation unit.
17374///
17375/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
17376/// function whose type is the sentinel, whose declarator is the structured
17377/// qualified name `namespace::a::b`, and whose body contains the namespace
17378/// items. Declaration indexing already reparses this bounded region. The
17379/// inverse scanner retains the original tree, so recover the same namespace
17380/// components from the declarator fields for its lexical-scope metadata.
17381fn cpp_root_namespace_sentinel<'tree>(
17382    node: Node<'tree>,
17383    source: &str,
17384    ancestry: &ParentIndex<'tree>,
17385) -> Option<CppNestedNamespaceSentinel<'tree>> {
17386    if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17387        return None;
17388    }
17389    let body = node.child_by_field_name("body")?;
17390    let declarator = node.child_by_field_name("declarator")?;
17391    let mut cursor = node.walk();
17392    let named = node
17393        .named_children(&mut cursor)
17394        .filter(|child| child.kind() != "comment")
17395        .collect::<Vec<_>>();
17396    if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17397        return None;
17398    }
17399    // The sentinel is the bogus return `type` (`type_identifier`), with the
17400    // literal `namespace` keyword leading the qualified declarator -- or,
17401    // under the macro-declaration grammar, a `macro_modifier`, with the
17402    // keyword occupying the type field (a keyword cannot be a type, which is
17403    // the same recovery evidence) and the namespace path as the declarator.
17404    let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17405        [first, second, _] => {
17406            let first_type = node.child_by_field_name("type")?;
17407            if !same_node(*first, first_type)
17408                || first_type.kind() != "type_identifier"
17409                || !same_node(*second, declarator)
17410                || !node.has_error()
17411            {
17412                return None;
17413            }
17414            (
17415                normalize_cpp_whitespace(node_text(first_type, source)),
17416                true,
17417            )
17418        }
17419        [first, second, third, _] => {
17420            let type_node = node.child_by_field_name("type")?;
17421            let identifier = macro_modifier_identifier(*first)?;
17422            if !same_node(*second, type_node)
17423                || type_node.kind() != "type_identifier"
17424                || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17425                || !same_node(*third, declarator)
17426            {
17427                return None;
17428            }
17429            (
17430                normalize_cpp_whitespace(node_text(identifier, source)),
17431                false,
17432            )
17433        }
17434        _ => return None,
17435    };
17436    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17437        return None;
17438    }
17439    if !matches!(
17440        declarator.kind(),
17441        "qualified_identifier" | "identifier" | "namespace_identifier"
17442    ) {
17443        return None;
17444    }
17445    let mut declarator_components = Vec::new();
17446    let mut valid_components = true;
17447    walk_named_tree_preorder(declarator, true, |component| {
17448        if !matches!(
17449            component.kind(),
17450            "identifier" | "namespace_identifier" | "type_identifier"
17451        ) {
17452            return WalkControl::Continue;
17453        }
17454        let Some(component) = canonical_cpp_qualified_component(component, source) else {
17455            valid_components = false;
17456            return WalkControl::Break;
17457        };
17458        declarator_components.push(component.name);
17459        WalkControl::SkipChildren
17460    });
17461    if strip_namespace_keyword {
17462        if !valid_components
17463            || declarator_components.first().map(String::as_str) != Some("namespace")
17464        {
17465            return None;
17466        }
17467        declarator_components.remove(0);
17468    }
17469    let namespace_components = declarator_components;
17470    if !valid_components
17471        || namespace_components.is_empty()
17472        || namespace_components
17473            .iter()
17474            .any(|component| component.is_empty() || cpp_export_macro_token(component))
17475    {
17476        return None;
17477    }
17478
17479    let mut cursor = body.walk();
17480    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17481        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17482            cpp_body_node(class_node).is_some()
17483                && class_like_name(class_node, source, ancestry)
17484                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
17485        })
17486    });
17487    if !has_complete_class
17488        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17489    {
17490        return None;
17491    }
17492
17493    Some(CppNestedNamespaceSentinel {
17494        function: node,
17495        body,
17496        namespace_components,
17497    })
17498}
17499
17500/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
17501/// malformed namespace-sentinel function.  The recovery is deliberately
17502/// structural: the class must be a direct body item, its own class node must be
17503/// erroneous and end before a unique anonymous `}` in the enclosing
17504/// declaration-list, and that namespace's next sibling must be a standalone
17505/// `;`.  The complete interior must pass the existing member-shaped reparse
17506/// gate. This avoids source brace scans and does not borrow a close from an
17507/// unrelated later declaration.
17508fn cpp_sentinel_fragmented_class_tail<'tree>(
17509    function: Node<'tree>,
17510    body: Node<'tree>,
17511    source: &str,
17512    ancestry: &ParentIndex<'tree>,
17513) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17514    let mut cursor = body.walk();
17515    let candidates = body
17516        .named_children(&mut cursor)
17517        .filter_map(|child| {
17518            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17519                let class_body = cpp_body_node(class_node)?;
17520                if !class_node.has_error() {
17521                    return None;
17522                }
17523                let name = class_like_name(class_node, source, ancestry)?;
17524                let raw_supertypes =
17525                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17526                        .then(|| extract_cpp_supertypes(class_node, source));
17527                return Some((
17528                    class_node,
17529                    template_node,
17530                    name,
17531                    class_body,
17532                    class_body.start_byte().checked_add(1)?,
17533                    raw_supertypes,
17534                ));
17535            }
17536            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17537            Some((
17538                child,
17539                None,
17540                prefix.name,
17541                prefix.open,
17542                prefix.open.end_byte(),
17543                prefix.raw_supertypes,
17544            ))
17545        })
17546        .collect::<Vec<_>>();
17547    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
17548        candidates.as_slice()
17549    else {
17550        return None;
17551    };
17552    if name.is_empty() || cpp_export_macro_token(name) {
17553        return None;
17554    }
17555
17556    let (close, semicolon) =
17557        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17558
17559    let reparse_end = close.start_byte();
17560    if *reparse_start >= reparse_end {
17561        return None;
17562    }
17563    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17564    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17565        return None;
17566    }
17567    let class_range = Range {
17568        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17569        end_byte: semicolon.end_byte(),
17570        start_line: template_node.map_or(class_node.start_position().row, |node| {
17571            node.start_position().row
17572        }) + 1,
17573        end_line: semicolon.end_position().row + 1,
17574    };
17575    Some(CppSentinelFragmentedClassTail {
17576        class_node: *class_node,
17577        template_node: *template_node,
17578        name: name.clone(),
17579        raw_supertypes: raw_supertypes.clone(),
17580        fragmented: FragmentedExportBody {
17581            reparse_start: *reparse_start,
17582            reparse_end,
17583            class_range,
17584        },
17585        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17586    })
17587}
17588
17589/// Recover the class and out-of-line owner scopes from every malformed
17590/// namespace-sentinel region in `root`.
17591///
17592/// This is the shared structural counterpart to
17593/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
17594/// reuses the visitor's sentinel/class admission predicates instead of parsing
17595/// source text a second time.  The returned values own only ranges and names, so
17596/// they can be retained by an inverted usage scan after the tree borrow ends.
17597pub fn cpp_sentinel_recovered_classes(
17598    root: Node<'_>,
17599    source: &str,
17600) -> Vec<CppSentinelRecoveredClass> {
17601    // The macro-declaration grammar absorbs the sentinel damage into structured
17602    // nodes and can leave the whole tree clean: `macro_modifier` and its kin are
17603    // admitted only where the older grammar would have reported an error, so a
17604    // clean tree carrying them holds the same recovery evidence. A tree with
17605    // neither errors nor macro-declaration nodes has no sentinel region.
17606    if !root.has_error()
17607        && !subtree_contains(root, |node| {
17608            matches!(
17609                node.kind(),
17610                "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
17611            )
17612        })
17613    {
17614        return Vec::new();
17615    }
17616    // This scan owns its walk of `root`, so it owns the parent index that walk
17617    // asks its ancestor questions through. Built after the error gate: a clean
17618    // tree without macro-declaration evidence returns without paying for one.
17619    let ancestry = ParentIndex::new(root);
17620    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
17621    let mut stack = vec![root];
17622    while let Some(current) = stack.pop() {
17623        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
17624            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
17625        {
17626            let namespace_components = cpp_sentinel_recovered_namespace_components(
17627                recovered.function,
17628                &recovered.namespace_components,
17629                source,
17630            );
17631            let fragmented = cpp_sentinel_fragmented_class_tail(
17632                recovered.function,
17633                recovered.body,
17634                source,
17635                &ancestry,
17636            );
17637            let mut class_candidates = Vec::new();
17638            let mut cursor = recovered.body.walk();
17639            for (class_node, template_node) in recovered
17640                .body
17641                .named_children(&mut cursor)
17642                .filter_map(cpp_sentinel_body_class_candidate)
17643            {
17644                let Some(name) = class_like_name(class_node, source, &ancestry) else {
17645                    continue;
17646                };
17647                if name.is_empty() || cpp_export_macro_token(&name) {
17648                    continue;
17649                }
17650                let is_fragmented = fragmented
17651                    .as_ref()
17652                    .is_some_and(|tail| same_node(tail.class_node, class_node));
17653                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
17654                    continue;
17655                }
17656                let class_range = if is_fragmented {
17657                    fragmented
17658                        .as_ref()
17659                        .map(|tail| tail.fragmented.class_range)
17660                        .expect("fragmented class range is present when class matches")
17661                } else {
17662                    cpp_declaration_range(template_node.unwrap_or(class_node))
17663                };
17664                class_candidates.push((class_range, name));
17665            }
17666            if let Some(fragmented) = fragmented
17667                .as_ref()
17668                .filter(|tail| tail.class_node.kind() == "ERROR")
17669            {
17670                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
17671            }
17672
17673            let mut owner_ranges =
17674                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
17675            cpp_sentinel_extend_unique_owner_ranges(
17676                &mut owner_ranges,
17677                cpp_sentinel_recovered_sibling_owner_ranges(
17678                    recovered.function,
17679                    &namespace_components,
17680                    source,
17681                ),
17682            );
17683            for (class_range, name) in class_candidates {
17684                push_cpp_sentinel_recovered_class(
17685                    &mut recovered_classes,
17686                    cpp_declaration_range(recovered.body),
17687                    &namespace_components,
17688                    class_range,
17689                    name,
17690                    &owner_ranges,
17691                );
17692            }
17693
17694            if let Some(declaration_list) = recovered
17695                .function
17696                .parent()
17697                .filter(|parent| parent.kind() == "declaration_list")
17698            {
17699                let outer_namespace =
17700                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
17701                push_cpp_sentinel_sibling_classes(
17702                    &mut recovered_classes,
17703                    declaration_list,
17704                    recovered.function,
17705                    &outer_namespace,
17706                    source,
17707                    &ancestry,
17708                );
17709            }
17710        } else if let Some(region) =
17711            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
17712        {
17713            let namespace_components = cpp_sentinel_recovered_namespace_components(
17714                current,
17715                &region.namespace_components,
17716                source,
17717            );
17718            let owner_container = current
17719                .parent()
17720                .filter(|parent| parent.kind() == "declaration_list")
17721                .unwrap_or(current);
17722            let owner_ranges =
17723                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
17724            push_cpp_sentinel_recovered_class(
17725                &mut recovered_classes,
17726                cpp_declaration_range(owner_container),
17727                &namespace_components,
17728                Range {
17729                    start_byte: region.class_start,
17730                    end_byte: region.class_close_end,
17731                    start_line: region.class_start_line,
17732                    end_line: region.class_close_line,
17733                },
17734                region.name,
17735                &owner_ranges,
17736            );
17737        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
17738            // A generic sentinel-prefixed class can be reduced as a malformed
17739            // function/ERROR without the explicit `namespace X` token pair.
17740            // Reuse the declaration visitor's bounded reparse and retain only
17741            // the recovered class identity/range here.
17742            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
17743                region;
17744            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
17745                continue;
17746            };
17747            let root = tree.root_node();
17748            let template_node = cpp_sentinel_reparsed_leading_template(root);
17749            // A region reparse is its own tree and needs its own parent index.
17750            let reparsed_ancestry = ParentIndex::new(root);
17751            let Some(reparsed_class) =
17752                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
17753            else {
17754                continue;
17755            };
17756            let class_node = reparsed_class.declaration_node;
17757            let name = reparsed_class.name;
17758            let namespace_components =
17759                cpp_sentinel_recovered_namespace_components(current, &[], source);
17760            let owner_container = current
17761                .parent()
17762                .filter(|parent| parent.kind() == "declaration_list")
17763                .unwrap_or(current);
17764            let mut owner_ranges =
17765                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
17766            cpp_sentinel_extend_unique_owner_ranges(
17767                &mut owner_ranges,
17768                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
17769            );
17770            push_cpp_sentinel_recovered_class(
17771                &mut recovered_classes,
17772                cpp_declaration_range(owner_container),
17773                &namespace_components,
17774                Range {
17775                    start_byte: class_start,
17776                    end_byte: close_end,
17777                    start_line: class_node.start_position().row + 1,
17778                    end_line: class_node.end_position().row + 1,
17779                },
17780                name,
17781                &owner_ranges,
17782            );
17783            if owner_container.kind() == "declaration_list" {
17784                push_cpp_sentinel_sibling_classes(
17785                    &mut recovered_classes,
17786                    owner_container,
17787                    current,
17788                    &namespace_components,
17789                    source,
17790                    &ancestry,
17791                );
17792            }
17793        }
17794
17795        let mut cursor = current.walk();
17796        stack.extend(current.named_children(&mut cursor));
17797    }
17798    // A shallower sentinel can expose nested classes as apparent namespace
17799    // siblings even after a deeper sentinel proves that a containing class
17800    // owns their ranges. Drop those shadow descriptors; scope recovery starts
17801    // from the proven containing class and appends parser-visible class
17802    // ancestors, preserving the full `Outer::Inner` chain.
17803    let shadowed = recovered_classes
17804        .iter()
17805        .map(|candidate| {
17806            recovered_classes.iter().any(|container| {
17807                container.class_range.start_byte <= candidate.class_range.start_byte
17808                    && container.class_range.end_byte >= candidate.class_range.end_byte
17809                    && container.class_range != candidate.class_range
17810                    && container.namespace_scope_components.len()
17811                        > candidate.namespace_scope_components.len()
17812                    && container
17813                        .namespace_scope_components
17814                        .starts_with(&candidate.namespace_scope_components)
17815            })
17816        })
17817        .collect::<Vec<_>>();
17818    let mut index = 0usize;
17819    recovered_classes.retain(|_| {
17820        let keep = !shadowed[index];
17821        index += 1;
17822        keep
17823    });
17824    recovered_classes
17825}
17826
17827/// A flat sentinel can swallow the first class while leaving later classes and
17828/// their out-of-line definitions as ordinary declaration-list siblings.  Once
17829/// the malformed class proves the sentinel envelope, retain those structurally
17830/// complete sibling classes under the same surviving namespace so every member
17831/// owner in the region uses one recovery contract.
17832fn push_cpp_sentinel_sibling_classes<'tree>(
17833    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
17834    declaration_list: Node<'tree>,
17835    sentinel_node: Node<'tree>,
17836    namespace_components: &[String],
17837    source: &str,
17838    ancestry: &ParentIndex<'tree>,
17839) {
17840    let owner_ranges =
17841        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
17842    let namespace_range = cpp_declaration_range(declaration_list);
17843    let mut cursor = declaration_list.walk();
17844    for (class_node, template_node) in declaration_list
17845        .named_children(&mut cursor)
17846        .filter(|child| !same_node(*child, sentinel_node))
17847        .filter_map(cpp_sentinel_body_class_candidate)
17848    {
17849        let Some(name) = class_like_name(class_node, source, ancestry) else {
17850            continue;
17851        };
17852        if name.is_empty()
17853            || cpp_export_macro_token(&name)
17854            || cpp_complete_class_body_close(class_node).is_none()
17855        {
17856            continue;
17857        }
17858        push_cpp_sentinel_recovered_class(
17859            recovered_classes,
17860            namespace_range,
17861            namespace_components,
17862            cpp_declaration_range(template_node.unwrap_or(class_node)),
17863            name,
17864            &owner_ranges,
17865        );
17866    }
17867}
17868
17869fn push_cpp_sentinel_recovered_class(
17870    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
17871    namespace_range: Range,
17872    namespace_components: &[String],
17873    class_range: Range,
17874    name: String,
17875    owner_ranges: &[CppSentinelRecoveredOwner],
17876) {
17877    let mut scope_components = namespace_components.to_vec();
17878    scope_components.push(name);
17879    let owner_ranges = owner_ranges
17880        .iter()
17881        .filter(|owner| owner.scope_components.starts_with(&scope_components))
17882        .cloned()
17883        .collect::<Vec<_>>();
17884    if recovered_classes.iter().any(|existing| {
17885        existing.class_range == class_range && existing.scope_components == scope_components
17886    }) {
17887        return;
17888    }
17889    recovered_classes.push(CppSentinelRecoveredClass {
17890        namespace_range,
17891        namespace_scope_components: namespace_components.to_vec(),
17892        class_range,
17893        scope_components,
17894        owner_ranges,
17895    });
17896}
17897
17898fn cpp_sentinel_recovered_namespace_components(
17899    function: Node<'_>,
17900    recovered_components: &[String],
17901    source: &str,
17902) -> Vec<String> {
17903    let mut ancestor_components = Vec::new();
17904    let mut ancestor = function.parent();
17905    while let Some(current) = ancestor {
17906        if current.kind() == "namespace_definition"
17907            && let Some(name_node) = current.child_by_field_name("name")
17908            && let Some(components) = cpp_name_components(name_node, source)
17909        {
17910            ancestor_components.push(
17911                components
17912                    .into_iter()
17913                    .map(|component| component.name)
17914                    .collect::<Vec<_>>(),
17915            );
17916        }
17917        ancestor = current.parent();
17918    }
17919    ancestor_components.reverse();
17920    let mut ancestors = ancestor_components
17921        .into_iter()
17922        .flatten()
17923        .collect::<Vec<_>>();
17924
17925    let overlap = (0..=ancestors.len().min(recovered_components.len()))
17926        .rev()
17927        .find(|length| {
17928            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
17929        })
17930        .unwrap_or(0);
17931    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
17932    ancestors
17933}
17934
17935fn cpp_sentinel_recovered_owner_ranges(
17936    body: Node<'_>,
17937    namespace_components: &[String],
17938    source: &str,
17939) -> Vec<CppSentinelRecoveredOwner> {
17940    let mut owners = Vec::new();
17941    walk_named_tree_preorder(body, true, |node| {
17942        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
17943    });
17944    owners
17945}
17946
17947fn cpp_sentinel_collect_owner_range(
17948    node: Node<'_>,
17949    namespace_components: &[String],
17950    source: &str,
17951    owners: &mut Vec<CppSentinelRecoveredOwner>,
17952) -> WalkControl {
17953    if node.kind() != "function_definition" {
17954        return WalkControl::Continue;
17955    }
17956    let Some(function_declarator) = extract_function_declarator(node) else {
17957        return WalkControl::Continue;
17958    };
17959    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
17960        return WalkControl::Continue;
17961    };
17962    let Some(mut components) = cpp_name_components(name_node, source) else {
17963        return WalkControl::Continue;
17964    };
17965    if components.len() <= 1 {
17966        return WalkControl::Continue;
17967    }
17968    components.pop();
17969    let mut owner_components = components
17970        .into_iter()
17971        .map(|component| component.name)
17972        .collect::<Vec<_>>();
17973    let overlap = (0..=namespace_components.len().min(owner_components.len()))
17974        .rev()
17975        .find(|length| {
17976            owner_components[..*length]
17977                == namespace_components[namespace_components.len().saturating_sub(*length)..]
17978        })
17979        .unwrap_or(0);
17980    let mut scope_components = namespace_components.to_vec();
17981    scope_components.extend(owner_components.drain(overlap..));
17982    if scope_components.len() <= namespace_components.len() {
17983        return WalkControl::Continue;
17984    }
17985    let range = cpp_declaration_range(node);
17986    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
17987        existing.range == range && existing.scope_components == scope_components
17988    }) {
17989        owners.push(CppSentinelRecoveredOwner {
17990            range,
17991            owner_name_start_byte: name_node.start_byte(),
17992            namespace_component_count: namespace_components.len(),
17993            scope_components,
17994        });
17995    }
17996    WalkControl::Continue
17997}
17998
17999fn cpp_sentinel_extend_unique_owner_ranges(
18000    owners: &mut Vec<CppSentinelRecoveredOwner>,
18001    additional: Vec<CppSentinelRecoveredOwner>,
18002) {
18003    for owner in additional {
18004        if !owners.iter().any(|existing| {
18005            existing.range == owner.range && existing.scope_components == owner.scope_components
18006        }) {
18007            owners.push(owner);
18008        }
18009    }
18010}
18011
18012fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18013    if node.kind() != "ERROR" || node.named_child_count() != 1 {
18014        return false;
18015    }
18016    let Some(end_name) = node.named_child(0) else {
18017        return false;
18018    };
18019    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18020        return false;
18021    }
18022    let mut cursor = node.walk();
18023    node.children(&mut cursor)
18024        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18025}
18026
18027/// Collect owner definitions that the malformed sentinel left as later
18028/// declaration-list siblings. Parser-visible namespace siblings are a hard
18029/// boundary: their declarations must keep their own lexical namespace.
18030fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18031    parent: Node<'_>,
18032    sentinel_node: Node<'_>,
18033    namespace_components: &[String],
18034    source: &str,
18035) -> Vec<CppSentinelRecoveredOwner> {
18036    let mut owners = Vec::new();
18037    let mut after_sentinel = false;
18038    let mut cursor = parent.walk();
18039    for child in parent.named_children(&mut cursor) {
18040        if !after_sentinel {
18041            if same_node(child, sentinel_node) {
18042                after_sentinel = true;
18043            }
18044            continue;
18045        }
18046        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18047    }
18048    owners
18049}
18050
18051/// Collect owner definitions after a malformed namespace, stopping only at
18052/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
18053/// enclosing container is not trusted to belong to the recovered namespace.
18054fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18055    parent: Node<'_>,
18056    sentinel_node: Node<'_>,
18057    namespace_components: &[String],
18058    source: &str,
18059) -> Option<Vec<CppSentinelRecoveredOwner>> {
18060    let mut owners = Vec::new();
18061    let mut after_namespace = false;
18062    let mut cursor = parent.walk();
18063    for child in parent.named_children(&mut cursor) {
18064        if !after_namespace {
18065            if same_node(child, sentinel_node) {
18066                after_namespace = true;
18067            }
18068            continue;
18069        }
18070        if cpp_sentinel_namespace_end(child, source) {
18071            return Some(owners);
18072        }
18073        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18074    }
18075    None
18076}
18077
18078fn cpp_sentinel_append_owner_ranges(
18079    sibling: Node<'_>,
18080    namespace_components: &[String],
18081    source: &str,
18082    owners: &mut Vec<CppSentinelRecoveredOwner>,
18083) {
18084    walk_named_tree_preorder(sibling, true, |node| {
18085        if node.kind() == "namespace_definition" {
18086            return WalkControl::SkipChildren;
18087        }
18088        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18089    });
18090}
18091
18092fn cpp_sentinel_recovered_sibling_owner_ranges(
18093    sentinel_node: Node<'_>,
18094    namespace_components: &[String],
18095    source: &str,
18096) -> Vec<CppSentinelRecoveredOwner> {
18097    let Some(declaration_list) = sentinel_node
18098        .parent()
18099        .filter(|parent| parent.kind() == "declaration_list")
18100    else {
18101        return Vec::new();
18102    };
18103    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18104        declaration_list,
18105        sentinel_node,
18106        namespace_components,
18107        source,
18108    );
18109
18110    let Some(namespace) = declaration_list
18111        .parent()
18112        .filter(|parent| parent.kind() == "namespace_definition")
18113    else {
18114        return owners;
18115    };
18116    let Some(outer_parent) = namespace.parent() else {
18117        return owners;
18118    };
18119    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18120        outer_parent,
18121        namespace,
18122        namespace_components,
18123        source,
18124    ) {
18125        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18126    }
18127    owners
18128}
18129
18130fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18131    let mut current = function_declarator.child_by_field_name("declarator")?;
18132    loop {
18133        if let Some(name) = macro_decorated_unqualified_name(current) {
18134            current = name;
18135            continue;
18136        }
18137        if matches!(
18138            current.kind(),
18139            "qualified_identifier"
18140                | "scoped_identifier"
18141                | "scoped_type_identifier"
18142                | "identifier"
18143                | "field_identifier"
18144                | "operator_name"
18145                | "destructor_name"
18146                | "literal_operator_name"
18147        ) {
18148            return Some(current);
18149        }
18150        current = current
18151            .child_by_field_name("declarator")
18152            .or_else(|| current.child_by_field_name("name"))
18153            .or_else(|| last_named_child(current))?;
18154    }
18155}
18156
18157/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
18158/// `::` between the halves. The scope is an attribute-like macro token, not
18159/// a namespace; `Name` is the declared name.
18160///
18161/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
18162/// tree-sitter joins the attribute macro and the constructor name into one
18163/// `qualified_identifier` and marks the separator it had to invent as MISSING.
18164/// That flag is the grammar's own record that no separator is in the source,
18165/// so read it instead of looking for `::` in the node text. A genuine
18166/// `Outer::Inner` keeps a present separator and is declined here, and so is
18167/// the explicit-global `::name`, whose `::` is present and whose scope is
18168/// absent.
18169fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18170    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18171        return None;
18172    }
18173    let mut cursor = node.walk();
18174    if node
18175        .children(&mut cursor)
18176        .any(|child| child.kind() == "::" && !child.is_missing())
18177    {
18178        return None;
18179    }
18180    node.child_by_field_name("name")
18181}
18182
18183fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18184    if let Some(name) = macro_decorated_unqualified_name(node) {
18185        return cpp_name_components(name, source);
18186    }
18187    match node.kind() {
18188        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18189            let mut components = match node.child_by_field_name("scope") {
18190                Some(scope) => cpp_name_components(scope, source)?,
18191                None => Vec::new(),
18192            };
18193            let name = node.child_by_field_name("name")?;
18194            components.push(canonical_cpp_qualified_component(name, source)?);
18195            Some(components)
18196        }
18197        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
18198    }
18199}
18200
18201fn cpp_sentinel_fragment_boundary<'tree>(
18202    function: Node<'tree>,
18203    class_node: Node<'tree>,
18204    class_body: Node<'tree>,
18205    source: &str,
18206) -> Option<(Node<'tree>, Node<'tree>)> {
18207    let declaration_list = function.parent()?;
18208    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18209        return None;
18210    }
18211    let namespace = declaration_list.parent()?;
18212    if namespace.kind() != "namespace_definition"
18213        || namespace.child_by_field_name("body") != Some(declaration_list)
18214    {
18215        return None;
18216    }
18217    let mut cursor = declaration_list.walk();
18218    let closes = declaration_list
18219        .children(&mut cursor)
18220        .filter(|child| {
18221            !child.is_named()
18222                && child.kind() == "}"
18223                && child.start_byte() >= function.end_byte()
18224                && child.start_byte() > class_node.end_byte()
18225                && child.start_byte() > class_body.start_byte()
18226        })
18227        .collect::<Vec<_>>();
18228    let [close] = closes.as_slice() else {
18229        return None;
18230    };
18231    let semicolon = namespace.next_named_sibling()?;
18232    if !cpp_is_stray_semicolon(semicolon, source)
18233        || close.end_byte() != namespace.end_byte()
18234        || semicolon.start_byte() < namespace.end_byte()
18235    {
18236        return None;
18237    }
18238    Some((*close, semicolon))
18239}
18240
18241/// Detect the bogus declaration/function tree that tree-sitter recovers for a
18242/// region prefixed by an object-like macro sentinel the parser cannot see
18243/// (issue #941), and return the byte range `[start, end)` of the swallowed
18244/// declaration interior to reparse.
18245///
18246/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
18247/// `function_definition` whose first non-comment named child is the sentinel
18248/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
18249/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
18250/// the real items.
18251/// `start` is the end of the sentinel identifier -- everything after it is the
18252/// genuine source. `end` is the node's end, extended across any trailing empty
18253/// `;` statement the mis-parse displaced past the node (the class/struct closing
18254/// semicolon), so the reparse sees a complete, brace-balanced item.
18255///
18256/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
18257/// node (`has_error`), or -- under the macro-declaration grammar, which parses
18258/// the sentinel region without recovery -- carry the sentinel as a
18259/// `macro_modifier`, a production admitted only where the parse would
18260/// otherwise contain an error. Unknown annotation/export macros can make a
18261/// real callable error-recovered even though tree-sitter still preserves its
18262/// declarator, so a preserved callable is admitted only when a displaced class
18263/// keyword precedes that declarator. The clean-reparse-to-items gate in
18264/// `cpp_reparsed_items_are_indexable` is the final arbiter.
18265/// Return the reparse start and, when present, the structurally recovered class
18266/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
18267/// separately from the reparse start because an opaque template-declaration
18268/// macro may precede it.
18269fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18270    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18271        return None;
18272    }
18273    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
18274    // retain a valid function declarator despite the unknown export macro making
18275    // the outer node erroneous. Remember that declarator for the ordering gate
18276    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
18277    // class keyword precedes a spurious callable assembled from a later member.
18278    let mut declarator_cursor = node.walk();
18279    let preserved_callable = node
18280        .children_by_field_name("declarator", &mut declarator_cursor)
18281        .find_map(extract_function_declarator);
18282    // Leading documentation comments are attached to the malformed
18283    // `function_definition` as named children.  They are not part of the
18284    // sentinel prefix, so select the first non-comment child structurally
18285    // rather than requiring the sentinel to be child zero.  This is the shape
18286    // emitted for nlohmann/json's `basic_json`: its class documentation comment
18287    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
18288    // envelope otherwise ends at the first nested union.
18289    let mut cursor = node.walk();
18290    let first = node
18291        .named_children(&mut cursor)
18292        .find(|child| child.kind() != "comment")?;
18293    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
18294    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
18295    // the same token is wrapped in. The wrapper is itself the recovery
18296    // evidence the older grammar's `has_error` carried, so a clean envelope is
18297    // admitted only in that form. The macro-declaration grammar can also keep
18298    // the sentinel as the `type` and absorb the displaced head that follows it
18299    // (`BEGIN_NS TEMPLATE_DECLARATION class Box { ... }`) into a
18300    // `macro_modifier_declarator` chain, which carries the same evidence.
18301    let (first_end, sentinel) = if first.kind() == "type_identifier" {
18302        if !node.has_error()
18303            && node
18304                .child_by_field_name("declarator")
18305                .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18306        {
18307            return None;
18308        }
18309        (
18310            first.end_byte(),
18311            normalize_cpp_whitespace(node_text(first, source)),
18312        )
18313    } else {
18314        let identifier = macro_modifier_identifier(first)?;
18315        (
18316            first.end_byte(),
18317            normalize_cpp_whitespace(node_text(identifier, source)),
18318        )
18319    };
18320    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18321        return None;
18322    }
18323    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
18324    // makes the trailing sentinel of one region and the leading sentinel of the
18325    // next both land as bare macro-token identifiers ahead of the real content.
18326    // Advance past every leading macro-token identifier so the reparse begins at
18327    // genuine source rather than another sentinel that would re-form the bogus
18328    // shape and fail the reparse gate.
18329    let mut start = first_end;
18330    let mut after_first = false;
18331    let mut cursor = node.walk();
18332    for child in node.named_children(&mut cursor) {
18333        if !after_first {
18334            if same_node(child, first) {
18335                after_first = true;
18336            }
18337            continue;
18338        }
18339        let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18340            Some(child)
18341        } else {
18342            macro_modifier_identifier(child)
18343        };
18344        if macro_token.is_some_and(|token| {
18345            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18346        }) {
18347            start = child.end_byte();
18348        } else {
18349            break;
18350        }
18351    }
18352    // An additional opaque template-declaration macro before a class can be
18353    // folded into the bogus function's qualified declarator.  In that shape
18354    // the macro is not a direct sibling we can skip above; tree-sitter exposes
18355    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
18356    // Reparse from that keyword (or a real preceding `template` keyword) so the
18357    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
18358    // a class nested in a genuine sentinel-wrapped namespace lies after the
18359    // body opening and must not change the established region start.
18360    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18361    let mut class_start = None;
18362    let mut template_start = None;
18363    let mut stack = vec![node];
18364    while let Some(current) = stack.pop() {
18365        if current.start_byte() >= prefix_end {
18366            continue;
18367        }
18368        if matches!(
18369            current.kind(),
18370            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18371        ) {
18372            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18373                "class" | "struct" | "union" | "enum" => {
18374                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18375                        seen.min(current.start_byte())
18376                    }));
18377                }
18378                "template" => {
18379                    template_start =
18380                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
18381                            seen.min(current.start_byte())
18382                        }));
18383                }
18384                _ => {}
18385            }
18386        }
18387        let mut cursor = current.walk();
18388        stack.extend(current.children(&mut cursor));
18389    }
18390    if preserved_callable.is_some_and(|callable| {
18391        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18392    }) {
18393        return None;
18394    }
18395    if let Some(class_start) = class_start {
18396        start = template_start
18397            .filter(|template_start| *template_start < class_start)
18398            .unwrap_or(class_start);
18399    }
18400    Some((start, class_start))
18401}
18402
18403/// Locate a sentinel-prefixed class whose malformed declaration was split across
18404/// root-level siblings. The true class close is represented structurally as a
18405/// lone `}` error followed by the class's displaced `;`; nested method/body
18406/// errors are not direct siblings of the sentinel node and therefore cannot
18407/// satisfy this pair.
18408fn cpp_sentinel_macro_class_region<'tree>(
18409    node: Node<'tree>,
18410    source: &str,
18411) -> Option<(usize, usize, usize, usize, usize, usize)> {
18412    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18413        return None;
18414    };
18415    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18416        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18417        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18418    if class_start >= body_open_start {
18419        return None;
18420    }
18421    let sibling_close = {
18422        let mut sibling = node.next_named_sibling();
18423        let mut found = None;
18424        while let Some(current) = sibling {
18425            let next = current.next_named_sibling();
18426            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18427                found = Some((
18428                    current.start_byte(),
18429                    semicolon.end_byte(),
18430                    semicolon.end_position().row + 1,
18431                ));
18432                break;
18433            }
18434            sibling = next;
18435        }
18436        found
18437    };
18438    // A stray `};` sibling is this class's close only when the bounded reparse
18439    // agrees the first body-bearing class ENDS there. When the malformed
18440    // envelope swallowed the class's true close, the scan can promote a much
18441    // later scope's close instead -- in protobuf-generated headers
18442    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
18443    // `struct TableStruct_*` paired with the first message class's `};`, making
18444    // the recovered "class body" span whole `namespace {}` blocks and minting
18445    // namespace-scope classes as nested members of the recovered class, which
18446    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
18447    // (#2275). On disagreement, fall through to the suffix-reparse boundary
18448    // below, which derives the close from the class node's own balanced body
18449    // range.
18450    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18451        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18452            return false;
18453        };
18454        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18455        // A region reparse is its own tree and needs its own parent index.
18456        let reparsed_ancestry = ParentIndex::new(tree.root_node());
18457        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18458            tree.root_node(),
18459            template_node,
18460            source,
18461            &reparsed_ancestry,
18462        ) else {
18463            return false;
18464        };
18465        let body = reparsed_class.body;
18466        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18467    });
18468    let (class_close_start, class_close_end, class_close_line) =
18469        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18470            (class_close_start, class_close_end, class_close_line)
18471        } else {
18472            // When the malformed envelope itself is an ERROR, tree-sitter can
18473            // leave the class's balanced close in the source while promoting
18474            // all following members to siblings. Reparse the complete suffix
18475            // and use the first body-bearing class node's own field range as
18476            // the partition boundary. This keeps balancing in tree-sitter and
18477            // preserves the source's original byte offsets.
18478            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18479            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18480            // A region reparse is its own tree and needs its own parent index.
18481            let reparsed_ancestry = ParentIndex::new(tree.root_node());
18482            let reparsed_class = cpp_sentinel_reparsed_class(
18483                tree.root_node(),
18484                template_node,
18485                source,
18486                &reparsed_ancestry,
18487            )?;
18488            let body = reparsed_class.body;
18489            let class_close_end = body.end_byte();
18490            let class_close_start = class_close_end.checked_sub(1)?;
18491            let class_close_line = body.end_position().row + 1;
18492            (class_close_start, class_close_end, class_close_line)
18493        };
18494    if class_close_start <= class_start {
18495        return None;
18496    }
18497
18498    // Reparse only far enough to expose the class body opening. This is a
18499    // structured check that the candidate really begins with a body-bearing
18500    // class-like item; the original malformed tree cannot provide that node.
18501    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18502    let class_root = tree.root_node();
18503    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18504    // A region reparse is its own tree and needs its own parent index.
18505    let reparsed_ancestry = ParentIndex::new(class_root);
18506    let reparsed_class =
18507        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18508    let body = reparsed_class.body;
18509    // The class body opening must agree with the malformed wrapper's structured
18510    // body field. This rejects an inner nested class while permitting later
18511    // members to remain fragmented as root-level siblings in the bounded parse.
18512    if body.start_byte() != body_open_start {
18513        return None;
18514    }
18515    let body_start = body.start_byte().checked_add(1)?;
18516    (body_start < class_close_start).then_some((
18517        reparse_start,
18518        class_start,
18519        body_start,
18520        class_close_start,
18521        class_close_end,
18522        class_close_line,
18523    ))
18524}
18525
18526/// Find the `{` token immediately following the class/struct/union/enum token
18527/// at `class_start` in the malformed tree. The token is anonymous in the C++
18528/// grammar, so this deliberately walks all children (not only named children)
18529/// and relies on sibling structure rather than source-text searching.
18530fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18531    let mut stack = vec![node];
18532    while let Some(current) = stack.pop() {
18533        if current.start_byte() == class_start
18534            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18535        {
18536            // The macro-declaration grammar keeps the class whole: the token's
18537            // specifier ancestor owns the body, and its `{` is the body's
18538            // first child rather than a later sibling of the token.
18539            let mut ancestor = current.parent();
18540            while let Some(parent) = ancestor {
18541                if matches!(
18542                    parent.kind(),
18543                    "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18544                ) {
18545                    if let Some(body) = cpp_body_node(parent) {
18546                        return body
18547                            .child(0)
18548                            .filter(|open| open.kind() == "{")
18549                            .map(|open| open.start_byte());
18550                    }
18551                    break;
18552                }
18553                if matches!(parent.kind(), "ERROR" | "declaration") {
18554                    break;
18555                }
18556                ancestor = parent.parent();
18557            }
18558            let mut sibling = current.next_sibling();
18559            while let Some(candidate) = sibling {
18560                if candidate.kind() == "{" {
18561                    return Some(candidate.start_byte());
18562                }
18563                sibling = candidate.next_sibling();
18564            }
18565        }
18566        let mut cursor = current.walk();
18567        stack.extend(current.children(&mut cursor));
18568    }
18569    None
18570}
18571
18572/// The class body that tree-sitter displaced out of a sentinel-prefixed
18573/// declaration and left as the malformed node's next sibling.
18574///
18575/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
18576/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
18577/// the last child of that `ERROR` and its `{` opens a sibling
18578/// `compound_statement` instead. The body is still the malformed tree's own
18579/// structured token, which is what the caller's `body.start_byte() !=
18580/// body_open_start` agreement check needs; it just is not reachable by walking
18581/// forward from the class token inside the node.
18582fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
18583    node.next_named_sibling()
18584        .filter(|sibling| sibling.kind() == "compound_statement")
18585}
18586
18587fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
18588    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
18589    let mut end = if class_start.is_some() {
18590        cpp_macro_prefixed_class_end(source, start)?
18591    } else {
18592        node.end_byte()
18593    };
18594    if class_start.is_none()
18595        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
18596    {
18597        end = end.max(namespace_end);
18598    }
18599    let mut sibling = node.next_named_sibling();
18600    while let Some(current) = sibling {
18601        if !cpp_is_stray_semicolon(current, source) {
18602            break;
18603        }
18604        end = current.end_byte();
18605        sibling = current.next_named_sibling();
18606    }
18607    (start < end).then_some((start, end))
18608}
18609
18610/// Extend a sentinel reparse through a following namespace that tree-sitter
18611/// flattened into the sentinel node's sibling list.
18612///
18613/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
18614/// unknown macro becomes a false function return type and consumes the first
18615/// namespace body. A second `namespace detail` then loses its enclosing node:
18616/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
18617/// attaches its declarations to the surrounding error tree. Reparse from that
18618/// structured keyword so tree-sitter, rather than a source-text brace scan,
18619/// supplies the complete namespace boundary.
18620fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
18621    let mut sibling = node.next_sibling();
18622    let keyword = loop {
18623        let candidate = sibling?;
18624        sibling = candidate.next_sibling();
18625        if candidate.kind() != "comment" {
18626            break candidate;
18627        }
18628    };
18629    if keyword.kind() != "namespace" {
18630        return None;
18631    }
18632    let name = loop {
18633        let candidate = sibling?;
18634        sibling = candidate.next_sibling();
18635        if candidate.kind() != "comment" {
18636            break candidate;
18637        }
18638    };
18639    if cpp_namespace_name_components(name, source).is_empty() {
18640        return None;
18641    }
18642    let open = loop {
18643        let candidate = sibling?;
18644        sibling = candidate.next_sibling();
18645        if candidate.kind() != "comment" {
18646            break candidate;
18647        }
18648    };
18649    if open.kind() != "{" {
18650        return None;
18651    }
18652
18653    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
18654    let root = tree.root_node();
18655    let mut cursor = root.walk();
18656    let namespace = root
18657        .named_children(&mut cursor)
18658        .find(|candidate| candidate.kind() != "comment")?;
18659    (namespace.kind() == "namespace_definition"
18660        && namespace.start_byte() == keyword.start_byte()
18661        && namespace.child_by_field_name("body").is_some())
18662    .then_some(namespace.end_byte())
18663}
18664
18665/// Parse the source suffix beginning at a structurally recovered class/template
18666/// keyword and return the end of its first body-bearing class item.  The parser,
18667/// rather than a brace scanner, owns nested-body balancing.  This is needed when
18668/// the original error tree truncates the class and scatters later members as
18669/// top-level siblings.
18670fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
18671    let tree = cpp_reparse_region_items(source, start, source.len())?;
18672    let root = tree.root_node();
18673    let mut cursor = root.walk();
18674    for item in root.named_children(&mut cursor) {
18675        if item.end_byte() <= start || item.kind() == "comment" {
18676            continue;
18677        }
18678        let mut stack = vec![item];
18679        while let Some(current) = stack.pop() {
18680            if matches!(
18681                current.kind(),
18682                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18683            ) && cpp_body_node(current).is_some()
18684            {
18685                return Some(current.end_byte());
18686            }
18687            let mut cursor = current.walk();
18688            stack.extend(current.named_children(&mut cursor));
18689        }
18690        // The recovered prefix is required to begin with the class item.  If
18691        // the first real item is something else, fail closed rather than skip
18692        // arbitrary source looking for a later class.
18693        return None;
18694    }
18695    None
18696}
18697
18698/// An empty `;` statement: the displaced closing semicolon of a struct/class that
18699/// the sentinel mis-parse split off past the bogus function node.
18700fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
18701    node.kind() == "expression_statement"
18702        && node.named_child_count() == 0
18703        && node_text(node, source).trim() == ";"
18704}
18705
18706fn cpp_stray_close_followed_by_semicolon<'tree>(
18707    close: Node<'tree>,
18708    next: Option<Node<'tree>>,
18709    source: &str,
18710) -> Option<Node<'tree>> {
18711    cpp_is_stray_close_brace(close, source)
18712        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
18713        .flatten()
18714}
18715
18716/// Recover the member that follows CPython's object header macro when
18717/// tree-sitter folds both declarations into one malformed field.
18718///
18719/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
18720/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
18721/// sole child is the real field declarator. The tail may wrap that identifier
18722/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
18723/// malformed wrappers do not provide the same evidence that this is the
18724/// CPython macro boundary.
18725#[derive(Clone, Copy)]
18726pub(crate) struct RecoveredPyObjectHeadField<'tree> {
18727    pub(crate) type_node: Node<'tree>,
18728    pub(crate) name: Node<'tree>,
18729    pub(crate) declarator: Node<'tree>,
18730}
18731
18732impl RecoveredPyObjectHeadField<'_> {
18733    pub(crate) fn pointer_depth(self) -> i32 {
18734        let mut depth = 0;
18735        let mut current = self.declarator;
18736        while current != self.name {
18737            debug_assert_eq!(current.kind(), "pointer_declarator");
18738            depth += 1;
18739            current = current
18740                .child_by_field_name("declarator")
18741                .expect("recovered PyObject field pointer has an inner declarator");
18742        }
18743        depth
18744    }
18745}
18746
18747pub(crate) fn recovered_pyobject_head_field<'tree>(
18748    node: Node<'tree>,
18749    source: &str,
18750) -> Option<RecoveredPyObjectHeadField<'tree>> {
18751    if node.kind() != "field_declaration" {
18752        return None;
18753    }
18754    let type_node = node.child_by_field_name("type")?;
18755    if type_node.kind() != "type_identifier"
18756        || node_text(type_node, source).trim() != "PyObject_HEAD"
18757    {
18758        return None;
18759    }
18760    let pseudo_declarator = node.child_by_field_name("declarator")?;
18761    let mut cursor = node.walk();
18762    let errors = node
18763        .named_children(&mut cursor)
18764        .filter(|child| child.kind() == "ERROR")
18765        .collect::<Vec<_>>();
18766    let [error] = errors.as_slice() else {
18767        return None;
18768    };
18769    if error.named_child_count() != 1 {
18770        return None;
18771    }
18772    let error_child = error.named_child(0)?;
18773    if pseudo_declarator.kind() == "field_identifier"
18774        && error.start_byte() >= pseudo_declarator.end_byte()
18775        && error_child.kind() == "identifier"
18776    {
18777        return Some(RecoveredPyObjectHeadField {
18778            type_node: pseudo_declarator,
18779            name: error_child,
18780            declarator: error_child,
18781        });
18782    }
18783    if pseudo_declarator.kind() != "pointer_declarator"
18784        || error.end_byte() > pseudo_declarator.start_byte()
18785        || error_child.kind() != "identifier"
18786    {
18787        return None;
18788    }
18789    let mut name = pseudo_declarator;
18790    while name.kind() == "pointer_declarator" {
18791        name = name.child_by_field_name("declarator")?;
18792    }
18793    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
18794        type_node: error_child,
18795        name,
18796        declarator: pseudo_declarator,
18797    })
18798}
18799
18800/// Recover the real field name when a leading object-like annotation macro
18801/// displaces a qualified type into tree-sitter's bit-field recovery shape.
18802///
18803/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
18804/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
18805/// `bitfield_clause` containing an error plus an assignment.  The assignment's
18806/// left field is the only structured declaration name in that malformed tail.
18807/// A real bit-field is excluded by the all-caps macro type and required error.
18808fn recovered_macro_qualified_field_clause<'tree>(
18809    node: Node<'tree>,
18810    source: &str,
18811) -> Option<Node<'tree>> {
18812    if node.kind() != "field_declaration" {
18813        return None;
18814    }
18815    let macro_type = node.child_by_field_name("type")?;
18816    if macro_type.kind() != "type_identifier"
18817        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
18818    {
18819        return None;
18820    }
18821    let mut cursor = node.walk();
18822    node.named_children(&mut cursor)
18823        .find(|child| child.kind() == "bitfield_clause")
18824}
18825
18826fn recovered_macro_qualified_field_declarators<'tree>(
18827    node: Node<'tree>,
18828    source: &str,
18829) -> Option<Vec<Node<'tree>>> {
18830    let pseudo_declarator = node.child_by_field_name("declarator")?;
18831    if pseudo_declarator.kind() != "field_identifier" {
18832        return None;
18833    }
18834    let clause = recovered_macro_qualified_field_clause(node, source)?;
18835    if !(0..clause.named_child_count()).any(|index| {
18836        clause
18837            .named_child(index)
18838            .is_some_and(|child| child.kind() == "ERROR")
18839    }) {
18840        return None;
18841    }
18842    let mut recovered = Vec::new();
18843    let mut stack = vec![clause];
18844    while let Some(current) = stack.pop() {
18845        if current.kind() == "assignment_expression"
18846            && let Some(left) = current.child_by_field_name("left")
18847            && extract_variable_name(left, source).is_some()
18848        {
18849            recovered.push(left);
18850            break;
18851        }
18852        let mut cursor = current.walk();
18853        stack.extend(current.named_children(&mut cursor));
18854    }
18855    if recovered.is_empty() {
18856        return None;
18857    }
18858    let mut cursor = node.walk();
18859    recovered.extend(
18860        node.children_by_field_name("declarator", &mut cursor)
18861            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
18862    );
18863    Some(recovered)
18864}
18865
18866/// Recover a macro-qualified constructor that tree-sitter represents as one
18867/// field declaration. The constructor call remains inside the direct recovery
18868/// error, while each member initializer becomes a false function declarator.
18869/// The class owner proves the constructor name and lets the caller ignore those
18870/// initializer declarators.
18871fn recovered_macro_qualified_constructor_call<'tree>(
18872    node: Node<'tree>,
18873    class_name: &str,
18874    source: &str,
18875) -> Option<Node<'tree>> {
18876    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
18877    let error = bitfield
18878        .named_child(0)
18879        .filter(|child| child.kind() == "ERROR")?;
18880    let mut stack = vec![error];
18881    while let Some(current) = stack.pop() {
18882        if current.kind() == "call_expression"
18883            && current
18884                .child_by_field_name("function")
18885                .is_some_and(|function| node_text(function, source) == class_name)
18886            && current
18887                .child_by_field_name("arguments")
18888                .is_some_and(|arguments| arguments.kind() == "argument_list")
18889        {
18890            return Some(current);
18891        }
18892        let mut cursor = current.walk();
18893        stack.extend(current.named_children(&mut cursor));
18894    }
18895    None
18896}
18897
18898/// The constructor head of a macro-decorated constructor that the
18899/// macro-declaration grammar reads as one field declaration
18900/// (`MSC_WARNING(suppress : 26495) CONSTEXPR Buffer(grow_fun grow) noexcept
18901/// : size_(size), grow_(grow) {}`, #1838). The macro is the field's `type`,
18902/// the constructor head is its first declarator (possibly inside a
18903/// `macro_modifier_declarator`), the first member initializer is a
18904/// `bitfield_clause` call, every later initializer is another function
18905/// declarator, and the body is the `default_value` initializer list. A
18906/// constructor declares no type, and no real field pairs a callable declarator
18907/// with a bitfield, so a head naming the enclosing class beside a bitfield
18908/// call is this shape. The caller publishes the head and none of the
18909/// initializer declarators.
18910fn recovered_macro_initialized_constructor_declarator<'tree>(
18911    node: Node<'tree>,
18912    class_name: &str,
18913    source: &str,
18914) -> Option<Node<'tree>> {
18915    if node.kind() != "field_declaration" {
18916        return None;
18917    }
18918    let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
18919    if cpp_function_declarator_name_node(constructor)
18920        .is_none_or(|name| node_text(name, source) != class_name)
18921    {
18922        return None;
18923    }
18924    let mut cursor = node.walk();
18925    let initializer = node
18926        .named_children(&mut cursor)
18927        .find(|child| child.kind() == "bitfield_clause")?
18928        .named_child(0)?;
18929    (initializer.kind() == "call_expression").then_some(constructor)
18930}
18931
18932/// Recover a macro-qualified member function declaration that tree-sitter
18933/// represents as a pseudo-field. An object-like export macro before a qualified
18934/// return type can displace the namespace and type into an ERROR/bitfield
18935/// recovery, leaving the callable as a structured `call_expression`.
18936///
18937/// The caller must route this shape before ordinary declarator classification;
18938/// otherwise the displaced namespace identifier is published as a field.
18939fn recovered_macro_qualified_function_call<'tree>(
18940    node: Node<'tree>,
18941    source: &str,
18942) -> Option<Node<'tree>> {
18943    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
18944    let declarator = node.child_by_field_name("declarator")?;
18945    if declarator.kind() != "field_identifier" {
18946        return None;
18947    }
18948    let mut cursor = node.walk();
18949    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
18950    if !named.iter().any(|child| {
18951        child.kind() == "storage_class_specifier"
18952            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
18953    }) {
18954        return None;
18955    }
18956    let mut bitfield_cursor = bitfield.walk();
18957    let payload = bitfield
18958        .named_children(&mut bitfield_cursor)
18959        .collect::<Vec<_>>();
18960    let [displaced_error, call] = payload.as_slice() else {
18961        return None;
18962    };
18963    if displaced_error.kind() != "ERROR"
18964        || displaced_error.named_child_count() != 1
18965        || displaced_error
18966            .named_child(0)
18967            .is_none_or(|child| child.kind() != "identifier")
18968        || call.kind() != "call_expression"
18969        || call
18970            .child_by_field_name("function")
18971            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
18972        || call
18973            .child_by_field_name("arguments")
18974            .is_none_or(|arguments| arguments.kind() != "argument_list")
18975    {
18976        return None;
18977    }
18978    Some(*call)
18979}
18980
18981/// The real constructor declarator of a macro-decorated constructor the
18982/// macro-declaration grammar reads as an explicit specifier, an `ERROR`
18983/// holding the macro and the constructor head as a `macro_modifier_declarator`,
18984/// and the member initializer mis-read as the function's own declarator
18985/// (`explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}`, #2552 shape 1).
18986/// The constructor head inside the `ERROR` is what names the class; the
18987/// declarator the `function_definition` fields is the initializer, never the
18988/// member.
18989fn recovered_macro_decorated_constructor_declarator<'tree>(
18990    node: Node<'tree>,
18991    class_name: &str,
18992    source: &str,
18993) -> Option<Node<'tree>> {
18994    if node.kind() != "function_definition" {
18995        return None;
18996    }
18997    let mut cursor = node.walk();
18998    let mut heads = Vec::new();
18999    for error in node
19000        .named_children(&mut cursor)
19001        .filter(|child| child.kind() == "ERROR")
19002    {
19003        let mut stack = vec![error];
19004        while let Some(current) = stack.pop() {
19005            let is_constructor_head = match current.kind() {
19006                "macro_modifier_declarator" | "function_declarator" => {
19007                    extract_function_declarator(current).is_some_and(|declarator| {
19008                        cpp_function_declarator_name_node(declarator)
19009                            .is_some_and(|name| node_text(name, source) == class_name)
19010                    })
19011                }
19012                _ => false,
19013            };
19014            if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19015                heads.push(declarator);
19016                continue;
19017            }
19018            let mut cursor = current.walk();
19019            stack.extend(current.named_children(&mut cursor));
19020        }
19021    }
19022    let [head] = heads.as_slice() else {
19023        return None;
19024    };
19025    Some(*head)
19026}
19027
19028fn recovered_macro_qualified_function_parameters(
19029    arguments: Node<'_>,
19030    source: &str,
19031) -> Option<(String, Vec<String>)> {
19032    if arguments.kind() != "argument_list" {
19033        return None;
19034    }
19035    let mut cursor = arguments.walk();
19036    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19037    if named.is_empty() {
19038        return Some(("()".to_string(), Vec::new()));
19039    }
19040    let mut types = Vec::new();
19041    let mut labels = Vec::new();
19042    let mut index = 0;
19043    while index < named.len() {
19044        let parameter_type = named[index];
19045        let parameter_name = named.get(index + 1).copied()?;
19046        if !matches!(
19047            parameter_type.kind(),
19048            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19049        ) || parameter_name.kind() != "ERROR"
19050            || parameter_name.named_child_count() != 1
19051            || parameter_name
19052                .named_child(0)
19053                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19054        {
19055            return None;
19056        }
19057        let parameter_name = parameter_name.named_child(0)?;
19058        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19059        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19060        index += 2;
19061    }
19062    Some((format!("({})", types.join(", ")), labels))
19063}
19064
19065/// Recognize the phantom field tree-sitter emits for a macro-qualified
19066/// function return type.  For example,
19067/// `static API result_type ThresholdForSmallA() { ... }` can become a
19068/// `field_declaration` (`API` as the type and `result_type` as a field name)
19069/// followed by a clean `function_definition` for `ThresholdForSmallA`.
19070///
19071/// Keep this predicate entirely tied to the CST envelope: the type must be an
19072/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
19073/// the declaration must carry a missing semicolon rather than a real one, and
19074/// the immediate named sibling must expose a function declarator.  A real
19075/// macro-decorated field with an explicit semicolon therefore remains a field.
19076pub fn recovered_macro_return_type_node<'tree>(
19077    node: Node<'tree>,
19078    source: &str,
19079) -> Option<Node<'tree>> {
19080    if node.kind() != "field_declaration" {
19081        return None;
19082    }
19083    let macro_type = node.child_by_field_name("type")?;
19084    if macro_type.kind() != "type_identifier"
19085        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19086    {
19087        return None;
19088    }
19089    let declarator = node.child_by_field_name("declarator")?;
19090    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19091        return None;
19092    }
19093    let mut has_missing_semicolon = false;
19094    let mut has_real_semicolon = false;
19095    for child in children_iter(node) {
19096        if child.kind() != ";" {
19097            continue;
19098        }
19099        if child.is_missing() {
19100            has_missing_semicolon = true;
19101        } else {
19102            has_real_semicolon = true;
19103        }
19104    }
19105    if !has_missing_semicolon || has_real_semicolon {
19106        return None;
19107    }
19108    let mut next = node.next_named_sibling();
19109    while next.is_some_and(|sibling| sibling.kind() == "comment") {
19110        next = next.and_then(|sibling| sibling.next_named_sibling());
19111    }
19112    let next = next?;
19113    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19114        return None;
19115    }
19116    let function_declarator = next.child_by_field_name("declarator")?;
19117    extract_function_declarator(function_declarator).map(|_| declarator)
19118}
19119
19120/// The omitted-semicolon class field the macro-declaration grammar folds into
19121/// the head of the normally typed function that follows it. The older grammar
19122/// left `DWORD preserved_field` ahead of `void PreserveFollowingFunction() {}`
19123/// as a damaged `field_declaration`; the macro-declaration grammar reads the
19124/// same pair as two leading `macro_modifier`s of the clean function
19125/// definition. Only an exact modifier pair ahead of a real type field and a
19126/// function declarator is a field: a lone modifier ahead of the type is the
19127/// macro-return shape (`static API result_type f() { ... }`), which carries
19128/// no field.
19129fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19130    if node.kind() != "function_definition" {
19131        return None;
19132    }
19133    let type_node = node.child_by_field_name("type")?;
19134    let declarator = node.child_by_field_name("declarator")?;
19135    extract_function_declarator(declarator)?;
19136    let mut cursor = node.walk();
19137    let head = node
19138        .named_children(&mut cursor)
19139        .take_while(|child| *child != type_node)
19140        .filter(|child| child.kind() != "comment")
19141        .collect::<Vec<_>>();
19142    let [type_modifier, name_modifier] = head.as_slice() else {
19143        return None;
19144    };
19145    macro_modifier_identifier(*type_modifier)?;
19146    macro_modifier_identifier(*name_modifier)?;
19147    Some((*type_modifier, *name_modifier))
19148}
19149
19150/// The stand-in field evidence in a clean macro-return function definition:
19151/// `CUTLASS_DEVICE static OperandLayout Layout() { ... }` has a
19152/// `macro_modifier` head and a return type naming an active template type
19153/// parameter. The older grammar read the same head as a phantom
19154/// `field_declaration` named after the type, and dependent calls like
19155/// `OperandLayout::packed` resolved to that field (#1129). Returns the head's
19156/// first token and the type node; the active-parameter gate is the same one
19157/// the phantom-field suppression used, so a macro return type naming no
19158/// template parameter (`result_type`) recovers nothing.
19159fn macro_return_template_parameter_field<'tree>(
19160    node: Node<'tree>,
19161    source: &str,
19162    ancestry: &ParentIndex<'tree>,
19163) -> Option<(Node<'tree>, Node<'tree>)> {
19164    if node.kind() != "function_definition" {
19165        return None;
19166    }
19167    let type_node = node.child_by_field_name("type")?;
19168    if type_node.kind() != "type_identifier" {
19169        return None;
19170    }
19171    let declarator = node.child_by_field_name("declarator")?;
19172    extract_function_declarator(declarator)?;
19173    let mut cursor = node.walk();
19174    let head = node
19175        .named_children(&mut cursor)
19176        .take_while(|child| *child != type_node)
19177        .filter(|child| child.kind() != "comment")
19178        .collect::<Vec<_>>();
19179    let [first, ..] = head.as_slice() else {
19180        return None;
19181    };
19182    if !head
19183        .iter()
19184        .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19185        || !head.iter().any(|child| child.kind() == "macro_modifier")
19186    {
19187        return None;
19188    }
19189    let name = normalize_cpp_whitespace(node_text(type_node, source));
19190    if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19191        return None;
19192    }
19193    Some((*first, type_node))
19194}
19195
19196/// Whether `name` is a type parameter of a template declaration that lexically
19197/// encloses `node`. The malformed macro-return field uses the parameter name as
19198/// its pseudo-declarator; preserving that field is necessary to publish a
19199/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
19200/// ancestors instead of interpreting source text so nested templates and
19201/// parser-recovered regions retain their real lexical scopes.
19202pub(crate) fn cpp_active_template_type_parameter<'tree>(
19203    node: Node<'tree>,
19204    name: &str,
19205    source: &str,
19206    ancestry: &ParentIndex<'tree>,
19207) -> bool {
19208    let mut ancestor = ancestry.parent(node);
19209    while let Some(current) = ancestor {
19210        if current.kind() == "template_declaration"
19211            && let Some(parameters) = current.child_by_field_name("parameters")
19212            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19213                cpp_template_parameter_name(parameter, source)
19214            })
19215            .type_parameter
19216        {
19217            return true;
19218        }
19219        ancestor = ancestry.parent(current);
19220    }
19221    false
19222}
19223
19224/// Reparse the region `[start, end)` of `source` as C++, confined to the region
19225/// via included ranges so every reparsed node keeps its original byte offset and
19226/// line number. The existing visitors read node text from the original source,
19227/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
19228/// `parse_rust_region_tree` technique.
19229fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19230    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19231}
19232
19233fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19234    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19235        return None;
19236    }
19237    let semicolon = node.next_sibling()?;
19238    if semicolon.kind() != ";" || semicolon.is_missing() {
19239        return None;
19240    }
19241    let row = node.start_position().row;
19242    let mut start = node.start_byte();
19243    let mut sibling = node.prev_sibling();
19244    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19245        if previous.kind() == ";" {
19246            break;
19247        }
19248        start = previous.start_byte();
19249        sibling = previous.prev_sibling();
19250    }
19251    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19252}
19253
19254/// One `RET name _(( args ));`-style K&R prototype-macro invocation
19255/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
19256/// byte spans a clean reparse needs, in original-source order.
19257struct PrototypeMacroCandidate {
19258    /// Where the return type and declared name begin.
19259    run_start: usize,
19260    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
19261    /// exclusive end of the first reparse span.
19262    identifier_start: usize,
19263    /// The inner `(`'s start byte: the start of the second reparse span.
19264    /// This paren is kept so the reparse sees exactly one parameter-list
19265    /// paren pair.
19266    inner_open_start: usize,
19267    /// Byte just past the inner `)`: the end of the second reparse span.
19268    inner_close_end: usize,
19269    /// Byte just past the outer `)`: the start of the third reparse span.
19270    outer_close_end: usize,
19271    /// Byte just past the terminating `;`: the end of the third reparse
19272    /// span, and of the whole candidate.
19273    semicolon_end: usize,
19274}
19275
19276impl PrototypeMacroCandidate {
19277    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
19278    /// one included-range tree: the return type and name, the parenthesized
19279    /// argument list, and the terminating `;`. The macro token and its outer
19280    /// wrapping parenthesis are omitted -- deleting them is exactly what the
19281    /// (absent) preprocessor macro expansion would do.
19282    fn ranges(&self) -> [(usize, usize); 3] {
19283        [
19284            (self.run_start, self.identifier_start),
19285            (self.inner_open_start, self.inner_close_end),
19286            (self.outer_close_end, self.semicolon_end),
19287        ]
19288    }
19289}
19290
19291/// A live terminating semicolon owned directly by `node`.
19292fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19293    node.child(node.child_count().checked_sub(1)?)
19294        .filter(|child| child.kind() == ";" && !child.is_missing())
19295}
19296
19297/// The known pre-ANSI prototype macros whose expansion is exactly their one
19298/// argument. The malformed CST cannot prove that an arbitrary identifier has
19299/// that definition, so unknown spellings fail closed rather than turning an
19300/// ordinary broken declaration into a Function (issue #2932).
19301fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19302    matches!(
19303        node.kind(),
19304        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19305    ) && matches!(
19306        normalize_cpp_whitespace(node_text(node, source)).as_str(),
19307        "_" | "__P" | "OF" | "PROTO"
19308    )
19309}
19310
19311/// Read tree-sitter's recovery for `declared_name MACRO`: a
19312/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
19313/// declared name, and whose `name` is a known prototype macro.
19314fn cpp_prototype_macro_qualified_parts<'tree>(
19315    node: Node<'tree>,
19316    source: &str,
19317) -> Option<(Node<'tree>, Node<'tree>)> {
19318    if node.kind() != "qualified_identifier" {
19319        return None;
19320    }
19321    let declared_name = node
19322        .child_by_field_name("scope")
19323        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19324    let macro_name = macro_decorated_unqualified_name(node)?;
19325    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19326}
19327
19328/// The inner parenthesized node of tree-sitter's structured
19329/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
19330/// the macro invocation's outer parentheses while retaining the one pair an
19331/// ordinary function declarator needs. No delimiter search is involved: both
19332/// pairs and their ownership come from the CST.
19333fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19334    if arguments.kind() != "argument_list"
19335        || arguments.named_child_count() != 1
19336        || arguments.child_count() != 3
19337        || arguments
19338            .child(0)
19339            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19340        || arguments
19341            .child(2)
19342            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19343    {
19344        return None;
19345    }
19346    let inner = arguments.named_child(0)?;
19347    let close_index = match inner.kind() {
19348        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19349        // tree-sitter completes the C++ cast production with a zero-width
19350        // value after the live close parenthesis.
19351        "cast_expression" => inner.child_count().checked_sub(2)?,
19352        _ => return None,
19353    };
19354    (inner
19355        .child(0)
19356        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19357        && inner
19358            .child(close_index)
19359            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19360    .then_some(inner)
19361}
19362
19363fn cpp_prototype_macro_candidate_from_init_declaration(
19364    declaration: Node<'_>,
19365    source: &str,
19366) -> Option<PrototypeMacroCandidate> {
19367    let init = declaration
19368        .child_by_field_name("declarator")
19369        .filter(|declarator| declarator.kind() == "init_declarator")?;
19370    let malformed_declarator = init.child_by_field_name("declarator")?;
19371    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19372        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19373    } else {
19374        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19375            return None;
19376        }
19377        let declared_name_error = init
19378            .prev_named_sibling()
19379            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19380        let declared_name = declared_name_error
19381            .named_child(0)
19382            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19383        (declared_name, malformed_declarator)
19384    };
19385    let arguments = init
19386        .child_by_field_name("value")
19387        .filter(|value| value.kind() == "argument_list")?;
19388    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19389    let semicolon = cpp_direct_semicolon(declaration)?;
19390    let return_type = declaration.child_by_field_name("type")?;
19391    if return_type.end_byte() > declared_name.start_byte()
19392        || declared_name.end_byte() > macro_name.start_byte()
19393        || macro_name.end_byte() > arguments.start_byte()
19394        || arguments.end_byte() > semicolon.start_byte()
19395    {
19396        return None;
19397    }
19398    Some(cpp_prototype_macro_candidate_from_parts(
19399        declaration.start_byte(),
19400        macro_name,
19401        inner,
19402        arguments,
19403        semicolon,
19404    ))
19405}
19406
19407fn cpp_prototype_macro_candidate_from_qualified_declaration(
19408    declaration: Node<'_>,
19409    source: &str,
19410) -> Option<PrototypeMacroCandidate> {
19411    let qualified = declaration
19412        .child_by_field_name("declarator")
19413        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19414    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19415    let open_error = qualified
19416        .next_named_sibling()
19417        .filter(|next| next.kind() == "ERROR")?;
19418    let close_error = last_named_child(declaration)
19419        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19420    if open_error.child_count() < 3
19421        || open_error
19422            .child(0)
19423            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19424        || open_error
19425            .child(1)
19426            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19427        || close_error.child_count() != 2
19428        || close_error
19429            .child(0)
19430            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19431        || close_error
19432            .child(1)
19433            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19434    {
19435        return None;
19436    }
19437    let inner_open = open_error.child(1)?;
19438    let inner_close = close_error.child(0)?;
19439    let outer_close = close_error.child(1)?;
19440    let semicolon = cpp_direct_semicolon(declaration)?;
19441    let return_type = declaration.child_by_field_name("type")?;
19442    if return_type.end_byte() > qualified.start_byte()
19443        || macro_name.end_byte() > open_error.start_byte()
19444        || inner_open.start_byte() > inner_close.end_byte()
19445        || inner_close.end_byte() > outer_close.start_byte()
19446        || outer_close.end_byte() > semicolon.start_byte()
19447    {
19448        return None;
19449    }
19450    Some(PrototypeMacroCandidate {
19451        run_start: declaration.start_byte(),
19452        identifier_start: macro_name.start_byte(),
19453        inner_open_start: inner_open.start_byte(),
19454        inner_close_end: inner_close.end_byte(),
19455        outer_close_end: outer_close.end_byte(),
19456        semicolon_end: semicolon.end_byte(),
19457    })
19458}
19459
19460fn cpp_prototype_macro_candidate_from_pointer_expression(
19461    statement: Node<'_>,
19462    source: &str,
19463) -> Option<PrototypeMacroCandidate> {
19464    if statement.kind() != "expression_statement"
19465        || statement.named_child_count() != 1
19466        || !statement.has_error()
19467    {
19468        return None;
19469    }
19470    let expansion = statement
19471        .named_child(0)
19472        .filter(|child| child.kind() == "parameter_pack_expansion")?;
19473    let binary = expansion
19474        .child_by_field_name("pattern")
19475        .filter(|pattern| pattern.kind() == "binary_expression")?;
19476    if binary.child_count() != 3
19477        || binary
19478            .child(1)
19479            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19480        || expansion
19481            .child(expansion.child_count().checked_sub(1)?)
19482            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19483    {
19484        return None;
19485    }
19486    let return_type = binary.child_by_field_name("left")?;
19487    let call = binary
19488        .child_by_field_name("right")
19489        .filter(|right| right.kind() == "call_expression")?;
19490    let qualified = call.child_by_field_name("function")?;
19491    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19492    let arguments = call
19493        .child_by_field_name("arguments")
19494        .filter(|arguments| arguments.kind() == "argument_list")?;
19495    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19496    let semicolon = cpp_direct_semicolon(statement)?;
19497    if return_type.end_byte() > declared_name.start_byte()
19498        || macro_name.end_byte() > arguments.start_byte()
19499        || arguments.end_byte() > semicolon.start_byte()
19500    {
19501        return None;
19502    }
19503    Some(cpp_prototype_macro_candidate_from_parts(
19504        statement.start_byte(),
19505        macro_name,
19506        inner,
19507        arguments,
19508        semicolon,
19509    ))
19510}
19511
19512/// The inner parenthesized declarator of the `((parameters))` wrapper the
19513/// macro-declaration grammar makes of a prototype macro's argument list, with
19514/// the same span-keeping contract as [`cpp_prototype_macro_inner_arguments`]:
19515/// both paren pairs and their ownership come from the CST, and the returned
19516/// node's exact range is the one pair an ordinary function declarator needs.
19517fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
19518    if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
19519        return None;
19520    }
19521    let open = outer.child(0)?;
19522    let close = outer.child(outer.child_count().checked_sub(1)?)?;
19523    if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
19524        return None;
19525    }
19526    let inner = outer.named_child(0)?;
19527    if inner.kind() != "parenthesized_declarator" {
19528        return None;
19529    }
19530    let inner_open = inner.child(0)?;
19531    let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
19532    (inner_open.kind() == "("
19533        && !inner_open.is_missing()
19534        && inner_close.kind() == ")"
19535        && !inner_close.is_missing())
19536    .then_some(inner)
19537}
19538
19539/// Whether the macro-declaration grammar admitted a word of this declaration
19540/// as a `macro_modifier`, the structured proof that the declaration did not
19541/// parse without it.
19542fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
19543    let mut cursor = declaration.walk();
19544    declaration
19545        .named_children(&mut cursor)
19546        .any(|child| child.kind() == "macro_modifier")
19547}
19548
19549/// `RET name _((args));` as the macro-declaration grammar reads it when the
19550/// argument list stays an expression: the return-type word is admitted as a
19551/// `macro_modifier`, the declared name takes the `type` field, and the macro
19552/// token is the init declarator's callee. Same #2932 contract as the
19553/// ERROR-based readers above, on nodes that carry no `ERROR` of their own.
19554fn cpp_prototype_macro_candidate_from_macro_modifier_init(
19555    declaration: Node<'_>,
19556    source: &str,
19557) -> Option<PrototypeMacroCandidate> {
19558    if !cpp_declaration_has_macro_modifier(declaration) {
19559        return None;
19560    }
19561    let declared_name = declaration
19562        .child_by_field_name("type")
19563        .filter(|type_node| type_node.kind() == "type_identifier")?;
19564    let init = declaration
19565        .child_by_field_name("declarator")
19566        .filter(|declarator| declarator.kind() == "init_declarator")?;
19567    let macro_name = init
19568        .child_by_field_name("declarator")
19569        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
19570    let arguments = init
19571        .child_by_field_name("value")
19572        .filter(|value| value.kind() == "argument_list")?;
19573    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19574    let semicolon = cpp_direct_semicolon(declaration)?;
19575    if declared_name.end_byte() > macro_name.start_byte()
19576        || macro_name.end_byte() > arguments.start_byte()
19577        || arguments.end_byte() > semicolon.start_byte()
19578    {
19579        return None;
19580    }
19581    Some(cpp_prototype_macro_candidate_from_parts(
19582        declaration.start_byte(),
19583        macro_name,
19584        inner,
19585        arguments,
19586        semicolon,
19587    ))
19588}
19589
19590/// `RET *name _((args));` as the macro-declaration grammar reads it when the
19591/// argument list stays declarator-shaped: the declared name and the macro
19592/// token prefix the nested parenthesized declarator as the modifiers of a
19593/// `macro_modifier_declarator` chain.
19594fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
19595    declaration: Node<'_>,
19596    source: &str,
19597) -> Option<PrototypeMacroCandidate> {
19598    let mut declarator = declaration.child_by_field_name("declarator")?;
19599    while declarator.kind() == "pointer_declarator" {
19600        declarator = declarator.child_by_field_name("declarator")?;
19601    }
19602    // The macro's own modifier must be preceded by the declared name's.
19603    let mut saw_name_modifier = false;
19604    let (macro_name, arguments) = loop {
19605        if declarator.kind() != "macro_modifier_declarator" {
19606            return None;
19607        }
19608        let mut cursor = declarator.walk();
19609        let modifier = declarator
19610            .named_children(&mut cursor)
19611            .find(|child| child.kind() == "macro_modifier")
19612            .and_then(macro_modifier_identifier)?;
19613        let inner = declarator.child_by_field_name("declarator")?;
19614        if cpp_is_prototype_macro_identifier(modifier, source) {
19615            break (modifier, inner);
19616        }
19617        saw_name_modifier = true;
19618        declarator = inner;
19619    };
19620    if !saw_name_modifier {
19621        return None;
19622    }
19623    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19624    let semicolon = cpp_direct_semicolon(declaration)?;
19625    Some(cpp_prototype_macro_candidate_from_parts(
19626        declaration.start_byte(),
19627        macro_name,
19628        inner,
19629        arguments,
19630        semicolon,
19631    ))
19632}
19633
19634/// `RET name _((args));` as the macro-declaration grammar reads it when the
19635/// macro token is the one word that could stand as the type: the return-type
19636/// words and the declared name are `macro_modifier`s and the nested argument
19637/// list is the whole declarator.
19638fn cpp_prototype_macro_candidate_from_macro_modifier_type(
19639    declaration: Node<'_>,
19640    source: &str,
19641) -> Option<PrototypeMacroCandidate> {
19642    if !cpp_declaration_has_macro_modifier(declaration) {
19643        return None;
19644    }
19645    let macro_name = declaration
19646        .child_by_field_name("type")
19647        .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
19648    let arguments = declaration.child_by_field_name("declarator")?;
19649    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
19650    let semicolon = cpp_direct_semicolon(declaration)?;
19651    Some(cpp_prototype_macro_candidate_from_parts(
19652        declaration.start_byte(),
19653        macro_name,
19654        inner,
19655        arguments,
19656        semicolon,
19657    ))
19658}
19659
19660/// `RET name _((args));` as the macro-declaration grammar reads it when the
19661/// argument list itself needs recovery: the return-type word is a
19662/// `macro_modifier`, the declared name takes the `type` field, the macro token
19663/// is the first declarator, and the argument list's paren pairs land in the
19664/// bracketing `ERROR`s.
19665fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
19666    declaration: Node<'_>,
19667    source: &str,
19668) -> Option<PrototypeMacroCandidate> {
19669    if !cpp_declaration_has_macro_modifier(declaration) {
19670        return None;
19671    }
19672    let declared_name = declaration
19673        .child_by_field_name("type")
19674        .filter(|type_node| type_node.kind() == "type_identifier")?;
19675    let mut declarator_cursor = declaration.walk();
19676    let macro_name = declaration
19677        .children_by_field_name("declarator", &mut declarator_cursor)
19678        .next()
19679        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
19680    let open_error = macro_name
19681        .next_named_sibling()
19682        .filter(|next| next.kind() == "ERROR")?;
19683    let close_error = last_named_child(declaration)
19684        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19685    if open_error.child_count() < 3
19686        || open_error
19687            .child(0)
19688            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19689        || open_error
19690            .child(1)
19691            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19692        || close_error.child_count() != 2
19693        || close_error
19694            .child(0)
19695            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19696        || close_error
19697            .child(1)
19698            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19699    {
19700        return None;
19701    }
19702    let inner_open = open_error.child(1)?;
19703    let inner_close = close_error.child(0)?;
19704    let outer_close = close_error.child(1)?;
19705    let semicolon = cpp_direct_semicolon(declaration)?;
19706    if declared_name.end_byte() > macro_name.start_byte()
19707        || macro_name.end_byte() > open_error.start_byte()
19708        || inner_open.start_byte() > inner_close.end_byte()
19709        || inner_close.end_byte() > outer_close.start_byte()
19710        || outer_close.end_byte() > semicolon.start_byte()
19711    {
19712        return None;
19713    }
19714    Some(PrototypeMacroCandidate {
19715        run_start: declaration.start_byte(),
19716        identifier_start: macro_name.start_byte(),
19717        inner_open_start: inner_open.start_byte(),
19718        inner_close_end: inner_close.end_byte(),
19719        outer_close_end: outer_close.end_byte(),
19720        semicolon_end: semicolon.end_byte(),
19721    })
19722}
19723
19724fn cpp_prototype_macro_candidate_from_parts(
19725    run_start: usize,
19726    macro_name: Node<'_>,
19727    inner_arguments: Node<'_>,
19728    outer_arguments: Node<'_>,
19729    semicolon: Node<'_>,
19730) -> PrototypeMacroCandidate {
19731    PrototypeMacroCandidate {
19732        run_start,
19733        identifier_start: macro_name.start_byte(),
19734        inner_open_start: inner_arguments.start_byte(),
19735        inner_close_end: inner_arguments.end_byte(),
19736        outer_close_end: outer_arguments.end_byte(),
19737        semicolon_end: semicolon.end_byte(),
19738    }
19739}
19740
19741/// Recover only CST shapes whose fields preserve the declaration name, macro
19742/// invocation, nested argument list, and terminator. Arbitrary flattened
19743/// `ERROR` wreckage stays unsupported rather than being interpreted through a
19744/// token or delimiter scan (issue #2932).
19745fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
19746    let candidate = match node.kind() {
19747        "declaration" => cpp_prototype_macro_candidate_from_init_declaration(node, source)
19748            .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
19749            .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
19750            .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source))
19751            .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
19752            .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source)),
19753        "expression_statement" => {
19754            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
19755        }
19756        _ => None,
19757    };
19758    candidate.into_iter().collect()
19759}
19760
19761fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
19762    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
19763        return false;
19764    }
19765    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
19766        return false;
19767    }
19768    let Some(declarator) = (if node.kind() == "function_definition" {
19769        node.child_by_field_name("declarator")
19770            .and_then(extract_function_declarator)
19771    } else {
19772        node.named_child(0)
19773            .filter(|child| child.kind() == "function_declarator")
19774    }) else {
19775        return false;
19776    };
19777    let Some(name) = cpp_function_declarator_name_node(declarator) else {
19778        return false;
19779    };
19780    declarator.start_byte() == node.start_byte()
19781        && name.kind() == "identifier"
19782        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
19783}
19784
19785/// Reparse a fragmented class-body interior while preserving its original byte
19786/// and line offsets, confined to the region by tree-sitter included ranges.
19787///
19788/// This used to materialize the region's whole file prefix as whitespace and
19789/// make the lexer walk it, the technique #1309 replaced on the other reparse
19790/// path: O(file) per fragmented-class recovery, on files that are already
19791/// error-recovered and already slow (#2788). Included ranges give the parser
19792/// the same view -- the region's bytes, at their original offsets and
19793/// line/column positions -- without materializing or lexing anything before it.
19794///
19795/// The equality of the two views is the claim, so a debug build parses both and
19796/// asserts the trees agree node for node.
19797fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
19798    let region = cpp_reparse_region_items(source, start, end);
19799
19800    #[cfg(debug_assertions)]
19801    assert_eq!(
19802        region.as_ref().map(cpp_tree_shape),
19803        cpp_reparse_padded_class_body(source, start, end)
19804            .as_ref()
19805            .map(cpp_tree_shape),
19806        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
19807         prefix produces"
19808    );
19809
19810    region
19811}
19812
19813/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
19814/// replaces, kept as the oracle a debug build asserts every region reparse
19815/// against and as the release-mode parity tests' reference (#2788).
19816#[cfg(any(debug_assertions, test))]
19817fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
19818    if start >= end {
19819        // The included-range parser refuses an empty region; the padded one
19820        // returned a tree holding nothing, which every caller read as "no
19821        // members here".
19822        return None;
19823    }
19824    let bytes = source.as_bytes();
19825    let prefix = bytes.get(..start)?;
19826    let interior = bytes.get(start..end)?;
19827    let mut padded = Vec::with_capacity(end);
19828    padded.extend(
19829        prefix
19830            .iter()
19831            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
19832    );
19833    padded.extend_from_slice(interior);
19834    let padded = String::from_utf8(padded).ok()?;
19835    let mut parser = tree_sitter::Parser::new();
19836    parser
19837        .set_language(&tree_sitter_cpp::LANGUAGE.into())
19838        .ok()?;
19839    parser.parse(&padded, None)
19840}
19841
19842/// Every node of `tree` in preorder, by kind, span and position, which is what
19843/// two reparses of one region have to agree on for their callers to read the
19844/// same declarations out of either (#2788).
19845#[cfg(any(debug_assertions, test))]
19846fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
19847    let mut shape = Vec::new();
19848    let mut cursor = tree.root_node().walk();
19849    let mut stack = vec![tree.root_node()];
19850    while let Some(node) = stack.pop() {
19851        shape.push((
19852            node.kind(),
19853            node.start_byte(),
19854            node.end_byte(),
19855            node.start_position().row,
19856            node.start_position().column,
19857            node.is_named(),
19858            node.is_missing(),
19859        ));
19860        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
19861        stack.extend(children.into_iter().rev());
19862    }
19863    shape
19864}
19865
19866/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
19867/// reparsed interior is indexed only when every top-level named node is a
19868/// well-formed C++ item (or a comment) and at least one real item is present.
19869/// Expression/statement soup surfaces as a top-level `ERROR` or
19870/// `expression_statement`, neither of which is an item kind, so it is rejected.
19871///
19872/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
19873/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
19874/// swallowed by a preceding dangling sentinel) reparses to a real
19875/// `namespace_definition` whose body still holds a bogus `function_definition`,
19876/// so the subtree legitimately carries an error. Container items are admitted
19877/// even with an internal error; the inner bogus function is recovered recursively
19878/// when `visit_function_definition` walks it. Each recursion strips at least one
19879/// leading sentinel, so the region strictly shrinks and recovery terminates.
19880///
19881/// A top-level `function_definition` is the one place we stay strict: it is
19882/// admitted only when it is clean or is itself a sentinel candidate. A function
19883/// that has an error and is not a sentinel is a real callable with a broken body,
19884/// so we refuse the whole reparse and let the ordinary path handle it (preserving
19885/// its real return type rather than re-deriving an implicit one).
19886fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
19887    let mut cursor = root.walk();
19888    let mut saw_item = false;
19889    for child in root.named_children(&mut cursor) {
19890        match child.kind() {
19891            "comment" => {}
19892            "function_definition" => {
19893                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
19894                    return false;
19895                }
19896                saw_item = true;
19897            }
19898            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
19899            _ => return false,
19900        }
19901    }
19902    saw_item
19903}
19904
19905/// Robustness gate for a reparsed fragmented multiple-base export class body
19906/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
19907/// kinds a class body produces when reparsed at translation-unit scope: the
19908/// access-specifier label preceding the first member surfaces as a
19909/// `labeled_statement` wrapping that member, and members surface as
19910/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
19911/// Statement or expression soup surfaces as other top-level kinds and is rejected,
19912/// so only a genuinely member-shaped body is ever re-owned as members; anything
19913/// ambiguous falls back to indexing the class alone.
19914fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
19915    if node.kind() != "ERROR" {
19916        return false;
19917    }
19918    let mut stack = Vec::new();
19919    let mut saw_function_declarator = false;
19920    let mut cursor = node.walk();
19921    for child in node.named_children(&mut cursor) {
19922        stack.push(child);
19923    }
19924    while let Some(current) = stack.pop() {
19925        match current.kind() {
19926            // Tree-sitter may wrap adjacent copy-control declarations in a
19927            // nested ERROR. Keep descending only through ERROR wrappers; the
19928            // actual declaration payload must be a function_declarator.
19929            "ERROR" => {
19930                let mut cursor = current.walk();
19931                stack.extend(current.named_children(&mut cursor));
19932            }
19933            "function_declarator" => saw_function_declarator = true,
19934            _ => return false,
19935        }
19936    }
19937    saw_function_declarator
19938}
19939
19940fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
19941    if node.kind() != "ERROR" {
19942        return false;
19943    }
19944    let mut cursor = node.walk();
19945    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19946    let [explicit, constructor_error, tail] = named.as_slice() else {
19947        return false;
19948    };
19949    if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
19950        return false;
19951    }
19952    // The copy-control declarators the inner ERROR holds, with only their `;`
19953    // tokens beside them. The older grammar kept the constructor alone there
19954    // and gave the destructor the slot after it; the macro-declaration grammar
19955    // absorbs the destructor beside the constructor and folds the member
19956    // written after them into the tail slot as a `macro_modifier_declarator`.
19957    let mut declarators = Vec::new();
19958    let mut stack = vec![*constructor_error];
19959    while let Some(current) = stack.pop() {
19960        match current.kind() {
19961            "ERROR" => {
19962                let mut cursor = current.walk();
19963                let children = current.named_children(&mut cursor).collect::<Vec<_>>();
19964                stack.extend(children.into_iter().rev());
19965            }
19966            "function_declarator" => declarators.push(current),
19967            _ => return false,
19968        }
19969    }
19970    let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
19971        ([constructor], "function_declarator") => (*constructor, *tail),
19972        ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
19973        _ => return false,
19974    };
19975    let Some(constructor_name) =
19976        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
19977    else {
19978        return false;
19979    };
19980    let Some(destructor_name) =
19981        extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
19982    else {
19983        return false;
19984    };
19985    let Some(destroyed_type) = destructor_name.named_child(0) else {
19986        return false;
19987    };
19988    constructor_name.kind() == "identifier"
19989        && destructor_name.kind() == "destructor_name"
19990        && destroyed_type.kind() == "identifier"
19991        && node_text(constructor_name, source) == node_text(destroyed_type, source)
19992}
19993
19994fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
19995    if node.kind() != "compound_statement" {
19996        return false;
19997    }
19998    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
19999        return false;
20000    };
20001    if prefix.kind() == "labeled_statement"
20002        && prefix.named_child(0).is_some_and(|label| {
20003            matches!(
20004                node_text(label, source).trim(),
20005                "public" | "private" | "protected"
20006            )
20007        })
20008    {
20009        return prefix.named_children(&mut prefix.walk()).any(|child| {
20010            child.kind() == "declaration"
20011                && child.has_error()
20012                && child
20013                    .named_children(&mut child.walk())
20014                    .any(cpp_reparsed_member_error_is_indexable)
20015        });
20016    }
20017    // A malformed constructor initializer can be split into a declaration
20018    // followed by its compound body when the class prefix already contains
20019    // realistic members. Keep this admission tied to that exact structured
20020    // declaration/error/body chain rather than accepting arbitrary blocks.
20021    prefix.kind() == "declaration"
20022        && prefix.has_error()
20023        && prefix
20024            .named_children(&mut prefix.walk())
20025            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20026}
20027
20028fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20029    if !cpp_reparsed_member_error_is_indexable(node) {
20030        return false;
20031    }
20032    let Some(preproc) = node.next_named_sibling() else {
20033        return false;
20034    };
20035    preproc.kind() == "preproc_if"
20036        && preproc.has_error()
20037        && preproc
20038            .named_children(&mut preproc.walk())
20039            .any(|child| child.kind() == "expression_statement" && child.has_error())
20040        && preproc
20041            .next_named_sibling()
20042            .is_some_and(|body| body.kind() == "compound_statement")
20043}
20044
20045/// Return a function body whose braces and ownership are explicit in the
20046/// reparsed class-member tree. An error below a real function envelope is
20047/// recoverable by the ordinary function visitor; a missing/deferred body is
20048/// not, because accepting it would let statement soup masquerade as a member.
20049fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20050    if node.kind() != "function_definition" {
20051        return None;
20052    }
20053    let body = node.child_by_field_name("body")?;
20054    if body.kind() != "compound_statement" {
20055        return None;
20056    }
20057    let open = body.child(0)?;
20058    let close = body.child(body.child_count().checked_sub(1)?)?;
20059    if open.kind() != "{"
20060        || open.is_missing()
20061        || close.kind() != "}"
20062        || close.is_missing()
20063        || close.end_byte() != body.end_byte()
20064        || body.end_byte() != node.end_byte()
20065    {
20066        return None;
20067    }
20068    Some(body)
20069}
20070
20071fn cpp_reparsed_member_function_errors_are_in_body(
20072    node: Node<'_>,
20073    body: Node<'_>,
20074    source: &str,
20075) -> bool {
20076    let mut cursor = node.walk();
20077    node.children(&mut cursor).all(|child| {
20078        same_node(child, body)
20079            || cpp_reparsed_member_attribute_error(child, source)
20080            || cpp_reparsed_member_signature_identifier_errors(child)
20081            || (!child.has_error() && !child.is_error() && !child.is_missing())
20082    })
20083}
20084
20085/// A complete callable can still carry parser errors in its signature when a
20086/// project annotation is not part of the C++ grammar (`nonneg int`,
20087/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
20088/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
20089/// leaves inside the already-proven callable envelope; structured statements,
20090/// literals, missing tokens, and other malformed signature payload remain
20091/// rejected.
20092fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20093    if !node.has_error() && !node.is_error() && !node.is_missing() {
20094        return false;
20095    }
20096    let mut stack = vec![node];
20097    let mut saw_error = false;
20098    while let Some(current) = stack.pop() {
20099        if current.is_missing() {
20100            return false;
20101        }
20102        if current.kind() == "ERROR" {
20103            saw_error = true;
20104            let mut cursor = current.walk();
20105            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20106            if children
20107                .iter()
20108                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20109            {
20110                return false;
20111            }
20112            stack.extend(children);
20113            continue;
20114        }
20115        let mut cursor = current.walk();
20116        stack.extend(current.children(&mut cursor));
20117    }
20118    saw_error
20119}
20120
20121fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20122    node.kind() == "ERROR"
20123        && node.named_child_count() == 1
20124        && node
20125            .named_child(0)
20126            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20127}
20128
20129fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20130    node.kind() == "identifier"
20131        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20132}
20133
20134/// A C++ attribute placed between a member's declarator and body can make
20135/// tree-sitter expose the callable as
20136/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
20137/// Keep this admission tied to that exact node geometry. In particular, an
20138/// arbitrary ERROR or identifier before a compound statement is not enough.
20139fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20140    let Some(body) = cpp_reparsed_member_function_body(node) else {
20141        return false;
20142    };
20143    let mut cursor = node.walk();
20144    let named = node
20145        .named_children(&mut cursor)
20146        .filter(|child| child.kind() != "comment")
20147        .collect::<Vec<_>>();
20148    let [type_node, error, attribute, body_node] = named.as_slice() else {
20149        return false;
20150    };
20151    if !same_node(*body_node, body)
20152        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20153        || !cpp_reparsed_export_macro_identifier(*attribute, source)
20154        || error.kind() != "ERROR"
20155        || error.named_child_count() != 1
20156    {
20157        return false;
20158    }
20159    error
20160        .named_child(0)
20161        .is_some_and(cpp_reparsed_attribute_callable_declarator)
20162}
20163
20164fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20165    cpp_structured_type_path(node, source).is_some()
20166        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20167}
20168
20169fn cpp_reparsed_member_return_and_declarator_are_indexable(
20170    return_error: Node<'_>,
20171    declarator: Node<'_>,
20172    body_node: Node<'_>,
20173    body: Node<'_>,
20174    source: &str,
20175) -> bool {
20176    return_error.kind() == "ERROR"
20177        && return_error.named_child_count() == 1
20178        && same_node(body_node, body)
20179        && return_error.named_child(0).is_some_and(|return_type| {
20180            cpp_reparsed_member_return_type_is_indexable(return_type, source)
20181        })
20182        && extract_function_declarator(declarator)
20183            .and_then(cpp_function_declarator_name_node)
20184            .is_some()
20185}
20186
20187fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20188    let Some(body) = cpp_reparsed_member_function_body(node) else {
20189        return false;
20190    };
20191    let mut cursor = node.walk();
20192    let named = node
20193        .named_children(&mut cursor)
20194        .filter(|child| child.kind() != "comment")
20195        .collect::<Vec<_>>();
20196    let [friend, return_error, declarator, body_node] = named.as_slice() else {
20197        return false;
20198    };
20199    friend.kind() == "type_identifier"
20200        && node_text(*friend, source) == "friend"
20201        && cpp_reparsed_member_return_and_declarator_are_indexable(
20202            *return_error,
20203            *declarator,
20204            *body_node,
20205            body,
20206            source,
20207        )
20208}
20209
20210fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20211    let Some(body) = cpp_reparsed_member_function_body(node) else {
20212        return false;
20213    };
20214    let mut cursor = node.walk();
20215    let named = node
20216        .named_children(&mut cursor)
20217        .filter(|child| child.kind() != "comment")
20218        .collect::<Vec<_>>();
20219    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20220        return false;
20221    };
20222    prefix
20223        .iter()
20224        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20225        && attribute.kind() == "type_identifier"
20226        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20227        && cpp_reparsed_member_return_and_declarator_are_indexable(
20228            *return_error,
20229            *declarator,
20230            *body_node,
20231            body,
20232            source,
20233        )
20234}
20235
20236/// An included-range reparse that begins inside a malformed class can merge an
20237/// access label and following template member. Tree-sitter then emits the label
20238/// as the `template_type` name, the template parameter list as its arguments,
20239/// an ERROR-wrapped return type, the callable declarator, and its complete
20240/// body. Admit only that exact structured displacement.
20241fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20242    let Some(body) = cpp_reparsed_member_function_body(node) else {
20243        return false;
20244    };
20245    let mut cursor = node.walk();
20246    let named = node
20247        .named_children(&mut cursor)
20248        .filter(|child| child.kind() != "comment")
20249        .collect::<Vec<_>>();
20250    // The member's return type is an `ERROR` of its own, or -- under the
20251    // macro-declaration grammar, which admits it as a modifier of the
20252    // declarator it could not separate -- folded into the declarator's
20253    // `macro_modifier_declarator` wrapper.
20254    let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20255        [template_type, return_error, declarator, body_node] => {
20256            let Some(return_type) = return_error.named_child(0) else {
20257                return false;
20258            };
20259            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20260                return false;
20261            }
20262            (*template_type, return_type, *declarator, *body_node)
20263        }
20264        [template_type, declarator, body_node] => {
20265            let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20266                .then(|| {
20267                    let mut cursor = declarator.walk();
20268                    declarator
20269                        .named_children(&mut cursor)
20270                        .find(|child| child.kind() == "macro_modifier")
20271                })
20272                .flatten()
20273                .and_then(macro_modifier_identifier)
20274            else {
20275                return false;
20276            };
20277            let Some(function) = declarator.child_by_field_name("declarator") else {
20278                return false;
20279            };
20280            (*template_type, return_type, function, *body_node)
20281        }
20282        _ => return false,
20283    };
20284    let Some(template_name) = template_type.child_by_field_name("name") else {
20285        return false;
20286    };
20287    let Some(arguments) = template_type.child_by_field_name("arguments") else {
20288        return false;
20289    };
20290    let mut cursor = template_type.walk();
20291    let template_errors = template_type
20292        .named_children(&mut cursor)
20293        .filter(|child| child.kind() == "ERROR")
20294        .collect::<Vec<_>>();
20295    let [comment_error] = template_errors.as_slice() else {
20296        return false;
20297    };
20298    let mut cursor = comment_error.walk();
20299    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20300    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20301        return false;
20302    };
20303    same_node(body_node, body)
20304        && template_type.kind() == "template_type"
20305        && template_name.kind() == "type_identifier"
20306        && matches!(
20307            node_text(template_name, source).trim(),
20308            "public" | "private" | "protected"
20309        )
20310        && arguments.kind() == "template_argument_list"
20311        && arguments.named_child_count() > 0
20312        && !arguments.has_error()
20313        && !colon.is_named()
20314        && colon.kind() == ":"
20315        && comments.iter().all(|child| child.kind() == "comment")
20316        && !template_keyword.is_named()
20317        && template_keyword.kind() == "template"
20318        && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20319        && extract_function_declarator(declarator)
20320            .and_then(cpp_function_declarator_name_node)
20321            .is_some()
20322}
20323
20324/// Return the constructor declaration tree-sitter can merge into an access
20325/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
20326/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
20327/// declaration's apparent type; the callable name must still exactly match the
20328/// recovered class, so unrelated labeled statements are never re-owned.
20329fn cpp_reparsed_preprocessor_constructor<'tree>(
20330    node: Node<'tree>,
20331    class_name: &str,
20332    source: &str,
20333) -> Option<Node<'tree>> {
20334    if node.kind() != "labeled_statement" {
20335        return None;
20336    }
20337    let mut cursor = node.walk();
20338    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20339    let [label, directive_error, declaration] = named.as_slice() else {
20340        return None;
20341    };
20342    if label.kind() != "statement_identifier"
20343        || !matches!(
20344            node_text(*label, source),
20345            "public" | "private" | "protected"
20346        )
20347        || directive_error.kind() != "ERROR"
20348        || directive_error.child_count() != 1
20349        || directive_error
20350            .child(0)
20351            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20352        || declaration.kind() != "declaration"
20353        || declaration.named_child_count() != 2
20354    {
20355        return None;
20356    }
20357    let apparent_type = declaration.child_by_field_name("type")?;
20358    if apparent_type.kind() != "type_identifier"
20359        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20360    {
20361        return None;
20362    }
20363    let declarator = declaration.child_by_field_name("declarator")?;
20364    let function = extract_function_declarator(declarator)?;
20365    let name = cpp_function_declarator_name_node(function)?;
20366    (node_text(name, source) == class_name).then_some(*declaration)
20367}
20368
20369fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20370    if extract_function_declarator(node)
20371        .and_then(cpp_function_declarator_name_node)
20372        .is_some()
20373    {
20374        return true;
20375    }
20376    node.kind() == "init_declarator"
20377        && node
20378            .child_by_field_name("declarator")
20379            .is_some_and(|declarator| declarator.kind() == "identifier")
20380        && node
20381            .child_by_field_name("value")
20382            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20383}
20384
20385/// Return true for the constrained/attribute form that tree-sitter splits into
20386/// an ERROR declaration, a preprocessor `requires` clause, and a following
20387/// compound statement. The three nodes must remain immediate named siblings;
20388/// this deliberately does not search source text or skip unrelated statements.
20389fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20390    if node.kind() != "ERROR" || node.named_child_count() != 3 {
20391        return false;
20392    }
20393    let mut cursor = node.walk();
20394    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20395    let [type_node, function_declarator, attribute] = named.as_slice() else {
20396        return false;
20397    };
20398    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20399        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20400        || attribute.kind() != "identifier"
20401        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20402    {
20403        return false;
20404    }
20405    let Some(preproc) =
20406        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20407    else {
20408        return false;
20409    };
20410    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20411        .filter(|sibling| sibling.kind() == "compound_statement")
20412    else {
20413        return false;
20414    };
20415    let Some(open) = body.child(0) else {
20416        return false;
20417    };
20418    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20419        return false;
20420    };
20421    let Some(condition) = preproc.child_by_field_name("condition") else {
20422        return false;
20423    };
20424    let mut cursor = preproc.walk();
20425    let payload = preproc
20426        .named_children(&mut cursor)
20427        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20428        .collect::<Vec<_>>();
20429    let [requires_statement] = payload.as_slice() else {
20430        return false;
20431    };
20432    let requires_clause = requires_statement.named_child(0);
20433
20434    open.kind() == "{"
20435        && !open.is_missing()
20436        && close.kind() == "}"
20437        && !close.is_missing()
20438        && close.end_byte() == body.end_byte()
20439        && requires_statement.kind() == "expression_statement"
20440        && requires_statement.named_child_count() == 1
20441        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20442}
20443
20444fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20445    let mut sibling = node.next_named_sibling();
20446    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20447        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20448    }
20449    sibling
20450}
20451
20452fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20453    let mut sibling = node.prev_named_sibling();
20454    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20455        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20456    }
20457    sibling
20458}
20459
20460fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20461    let Some(preproc) =
20462        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20463    else {
20464        return false;
20465    };
20466    let Some(error) =
20467        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20468    else {
20469        return false;
20470    };
20471    cpp_reparsed_attribute_requires_error(error, source)
20472}
20473
20474fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20475    parameter: Node<'tree>,
20476    macro_name: Node<'tree>,
20477    message: Node<'tree>,
20478    source: &str,
20479) -> Option<Node<'tree>> {
20480    let parameter_name = parameter.named_child(0)?;
20481    (parameter.kind() == "type_parameter_declaration"
20482        && parameter_name.kind() == "type_identifier"
20483        && matches!(macro_name.kind(), "identifier" | "type_identifier")
20484        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20485        && message.kind() == "string_literal")
20486        .then_some(parameter_name)
20487}
20488
20489fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
20490    node: Node<'tree>,
20491    source: &str,
20492) -> Option<Node<'tree>> {
20493    if node.kind() != "ERROR" {
20494        return None;
20495    }
20496    let mut cursor = node.walk();
20497    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20498    let [parameter, macro_name, message] = named.as_slice() else {
20499        return None;
20500    };
20501    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
20502}
20503
20504/// Recognize the alternate constraint-macro prefix where tree-sitter retains
20505/// the complete qualified constraint as a fourth child instead of moving it
20506/// into the following function. Keep the gate tied to a two-type template
20507/// constraint that names the declared type parameter.
20508fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
20509    node: Node<'tree>,
20510    source: &str,
20511) -> Option<Node<'tree>> {
20512    if node.kind() != "ERROR" {
20513        return None;
20514    }
20515    let mut cursor = node.walk();
20516    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20517    let [parameter, macro_name, message, constraint] = named.as_slice() else {
20518        return None;
20519    };
20520    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
20521        *parameter,
20522        *macro_name,
20523        *message,
20524        source,
20525    )?;
20526    let constraint_scope = constraint.child_by_field_name("scope")?;
20527    let constraint_template = constraint.child_by_field_name("name")?;
20528    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20529    let mut argument_cursor = constraint_arguments.walk();
20530    let constraint_types = constraint_arguments
20531        .named_children(&mut argument_cursor)
20532        .collect::<Vec<_>>();
20533    if constraint.kind() != "qualified_identifier"
20534        || constraint_scope.kind() != "namespace_identifier"
20535        || !matches!(
20536            constraint_template.kind(),
20537            "template_function" | "template_type"
20538        )
20539        || !matches!(constraint_types.as_slice(), [left, right]
20540            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20541        || constraint_arguments.has_error()
20542    {
20543        return None;
20544    }
20545    let parameter_text = node_text(parameter_name, source);
20546    let mut stack = constraint_types;
20547    while let Some(current) = stack.pop() {
20548        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
20549            return Some(parameter_name);
20550        }
20551        let mut cursor = current.walk();
20552        stack.extend(current.named_children(&mut cursor));
20553    }
20554    None
20555}
20556
20557fn cpp_reparsed_template_macro_companion_is_indexable(
20558    node: Node<'_>,
20559    parameter_name: Node<'_>,
20560    source: &str,
20561) -> bool {
20562    let Some(body) = cpp_reparsed_member_function_body(node) else {
20563        return false;
20564    };
20565    let mut cursor = node.walk();
20566    let named = node
20567        .named_children(&mut cursor)
20568        .filter(|child| child.kind() != "comment")
20569        .collect::<Vec<_>>();
20570    // The member's return type is an `ERROR` of its own, or -- under the
20571    // macro-declaration grammar, which admits it as a modifier of the
20572    // declarator it could not separate -- folded into the declarator's
20573    // `macro_modifier_declarator` wrapper.
20574    let (constraint, close_error, storage, return_type, declarator, body_node) = match named
20575        .as_slice()
20576    {
20577        [
20578            constraint,
20579            close_error,
20580            storage,
20581            return_error,
20582            declarator,
20583            body_node,
20584        ] => {
20585            let Some(return_type) = return_error.named_child(0) else {
20586                return false;
20587            };
20588            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20589                return false;
20590            }
20591            (
20592                *constraint,
20593                *close_error,
20594                *storage,
20595                return_type,
20596                *declarator,
20597                *body_node,
20598            )
20599        }
20600        [constraint, close_error, storage, declarator, body_node] => {
20601            let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
20602                return false;
20603            };
20604            (
20605                *constraint,
20606                *close_error,
20607                *storage,
20608                return_type,
20609                function,
20610                *body_node,
20611            )
20612        }
20613        _ => return false,
20614    };
20615    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
20616        return false;
20617    };
20618    let Some(constraint_template) = constraint.child_by_field_name("name") else {
20619        return false;
20620    };
20621    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
20622        return false;
20623    };
20624    let mut cursor = constraint_arguments.walk();
20625    let constraint_types = constraint_arguments
20626        .named_children(&mut cursor)
20627        .collect::<Vec<_>>();
20628    same_node(body_node, body)
20629        && constraint.kind() == "qualified_identifier"
20630        && constraint_scope.kind() == "namespace_identifier"
20631        && constraint_template.kind() == "template_type"
20632        && matches!(constraint_types.as_slice(), [left, right]
20633            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20634        && !constraint_arguments.has_error()
20635        && close_error.kind() == "ERROR"
20636        && close_error.named_child_count() == 0
20637        && storage.kind() == "storage_class_specifier"
20638        && return_type.kind() == "identifier"
20639        && node_text(return_type, source) == node_text(parameter_name, source)
20640        && extract_function_declarator(declarator)
20641            .and_then(cpp_function_declarator_name_node)
20642            .is_some()
20643}
20644
20645fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
20646    node: Node<'tree>,
20647    parameter_name: Node<'_>,
20648    source: &str,
20649) -> Option<Node<'tree>> {
20650    let body = cpp_reparsed_member_function_body(node)?;
20651    let constraint = node.child_by_field_name("type")?;
20652    let constraint_template = constraint.child_by_field_name("name")?;
20653    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
20654    let mut argument_cursor = constraint_arguments.walk();
20655    let constraint_types = constraint_arguments
20656        .named_children(&mut argument_cursor)
20657        .collect::<Vec<_>>();
20658    if constraint.kind() != "qualified_identifier"
20659        || constraint_template.kind() != "template_type"
20660        || !matches!(constraint_types.as_slice(), [left, right]
20661            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
20662        || constraint_arguments.has_error()
20663        || node
20664            .child_by_field_name("body")
20665            .is_none_or(|candidate| !same_node(candidate, body))
20666    {
20667        return None;
20668    }
20669
20670    let mut cursor = node.walk();
20671    let recovery_errors = node
20672        .named_children(&mut cursor)
20673        .filter(|child| child.kind() == "ERROR")
20674        .collect::<Vec<_>>();
20675    if !recovery_errors
20676        .iter()
20677        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
20678        || !recovery_errors.iter().all(|error| {
20679            error.named_child_count() == 0
20680                || cpp_reparsed_constraint_macro_error(*error, source)
20681                || (error.named_child_count() == 1
20682                    && error
20683                        .named_child(0)
20684                        .is_some_and(|child| child.kind() == "function_declarator"))
20685        })
20686    {
20687        return None;
20688    }
20689
20690    let parameter_text = node_text(parameter_name, source);
20691    let mut declarators = node
20692        .child_by_field_name("declarator")
20693        .and_then(extract_function_declarator)
20694        .into_iter()
20695        .collect::<Vec<_>>();
20696    for error in recovery_errors {
20697        let mut stack = vec![error];
20698        while let Some(current) = stack.pop() {
20699            if current.kind() == "function_declarator" {
20700                declarators.push(current);
20701            }
20702            let mut cursor = current.walk();
20703            stack.extend(current.named_children(&mut cursor));
20704        }
20705    }
20706    declarators.into_iter().find(|declarator| {
20707        cpp_function_declarator_name_node(*declarator)
20708            .is_some_and(|name| name.kind() == "identifier")
20709            && declarator
20710                .child_by_field_name("parameters")
20711                .is_some_and(|parameters| {
20712                    parameters
20713                        .named_children(&mut parameters.walk())
20714                        .filter_map(|parameter| parameter.child_by_field_name("type"))
20715                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
20716                })
20717    })
20718}
20719
20720fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
20721    node: Node<'_>,
20722    parameter_name: Node<'_>,
20723    source: &str,
20724) -> bool {
20725    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
20726}
20727
20728fn cpp_reparsed_template_macro_function_companion_is_indexable(
20729    node: Node<'_>,
20730    parameter_name: Node<'_>,
20731    source: &str,
20732) -> bool {
20733    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
20734        return false;
20735    }
20736    let Some(return_type) = node.child_by_field_name("type") else {
20737        return false;
20738    };
20739    let Some(function_declarator) = node
20740        .child_by_field_name("declarator")
20741        .and_then(extract_function_declarator)
20742    else {
20743        return false;
20744    };
20745    if cpp_function_declarator_name_node(function_declarator).is_none()
20746        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
20747    {
20748        return false;
20749    }
20750    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
20751        return false;
20752    };
20753    let parameter_text = node_text(parameter_name, source);
20754    parameters
20755        .named_children(&mut parameters.walk())
20756        .any(|parameter| {
20757            parameter
20758                .child_by_field_name("type")
20759                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
20760        })
20761}
20762
20763fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
20764    if node.kind() != "ERROR" {
20765        return false;
20766    }
20767    let mut stack = vec![node];
20768    while let Some(current) = stack.pop() {
20769        let macro_shape = match current.kind() {
20770            "call_expression" => current
20771                .child_by_field_name("function")
20772                .zip(current.child_by_field_name("arguments")),
20773            "init_declarator" => current
20774                .child_by_field_name("declarator")
20775                .zip(current.child_by_field_name("value")),
20776            _ => None,
20777        };
20778        if let Some((name, arguments)) = macro_shape
20779            && name.kind() == "identifier"
20780            && arguments.kind() == "argument_list"
20781            && arguments.named_child_count() >= 2
20782            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20783        {
20784            return true;
20785        }
20786        let mut cursor = current.walk();
20787        stack.extend(current.named_children(&mut cursor));
20788    }
20789    false
20790}
20791
20792fn cpp_recovered_template_macro_constructor<'tree>(
20793    node: Node<'tree>,
20794    source: &str,
20795) -> Option<(Node<'tree>, Node<'tree>)> {
20796    let mut prefix = node.prev_named_sibling()?;
20797    while prefix.kind() == "comment" {
20798        prefix = prefix.prev_named_sibling()?;
20799    }
20800    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
20801    let parameter = parameter_name
20802        .parent()
20803        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
20804    let declarator =
20805        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
20806    Some((declarator, parameter))
20807}
20808
20809fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
20810    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
20811        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
20812            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
20813                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
20814                    function,
20815                    parameter_name,
20816                    source,
20817                )
20818        });
20819    }
20820    let Some(parameter_name) =
20821        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
20822    else {
20823        return false;
20824    };
20825    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
20826        cpp_reparsed_template_macro_function_companion_is_indexable(
20827            function,
20828            parameter_name,
20829            source,
20830        )
20831    })
20832}
20833
20834fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20835    let function_name = node
20836        .child_by_field_name("declarator")
20837        .and_then(extract_function_declarator)
20838        .and_then(cpp_function_declarator_name_node);
20839    if let Some(body) = cpp_reparsed_member_function_body(node)
20840        && function_name.is_some()
20841        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
20842    {
20843        return true;
20844    }
20845    cpp_reparsed_attribute_member_function(node, source)
20846        || cpp_reparsed_friend_function_is_indexable(node, source)
20847        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
20848        || cpp_reparsed_access_template_function_is_indexable(node, source)
20849        || cpp_recovered_template_macro_constructor(node, source).is_some()
20850}
20851
20852/// Recognize the three top-level nodes produced when an unknown attribute
20853/// macro separates an inline member's declarator from its body in a reparsed
20854/// class interior: an errorful declaration with a missing semicolon, the macro
20855/// call expression, and the complete compound body. Their adjacency and exact
20856/// structured shapes prove one recoverable member envelope; arbitrary calls or
20857/// blocks do not pass this gate.
20858fn cpp_reparsed_macro_attribute_member_sequence(
20859    children: &[Node<'_>],
20860    index: usize,
20861    source: &str,
20862) -> bool {
20863    let Some(prefix) = children.get(index).copied() else {
20864        return false;
20865    };
20866    let declaration = if prefix.kind() == "labeled_statement" {
20867        prefix
20868            .named_child(prefix.named_child_count().saturating_sub(1))
20869            .filter(|child| child.kind() == "declaration")
20870    } else {
20871        (prefix.kind() == "declaration").then_some(prefix)
20872    };
20873    let Some(declaration) = declaration else {
20874        return false;
20875    };
20876    if !declaration.has_error()
20877        || declaration
20878            .child_by_field_name("declarator")
20879            .and_then(extract_function_declarator)
20880            .and_then(cpp_function_declarator_name_node)
20881            .is_none()
20882    {
20883        return false;
20884    }
20885    let Some(attribute_statement) = children.get(index + 1).copied() else {
20886        return false;
20887    };
20888    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
20889        .then(|| attribute_statement.named_child(0))
20890        .flatten()
20891        .filter(|child| child.kind() == "call_expression")
20892    else {
20893        return false;
20894    };
20895    let Some(attribute_name) = attribute_call
20896        .child_by_field_name("function")
20897        .filter(|function| function.kind() == "identifier")
20898        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
20899    else {
20900        return false;
20901    };
20902    if !cpp_export_macro_token(&attribute_name) {
20903        return false;
20904    }
20905    let Some(body) = children.get(index + 2).copied() else {
20906        return false;
20907    };
20908    body.kind() == "compound_statement"
20909        && body.child(0).is_some_and(|open| open.kind() == "{")
20910        && body
20911            .child(body.child_count().saturating_sub(1))
20912            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
20913        && declaration.end_byte() <= attribute_statement.start_byte()
20914        && attribute_statement.end_byte() <= body.start_byte()
20915}
20916
20917/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
20918/// types and specifiers followed by a declarator, over and over, with nothing
20919/// left over. A string-argument attribute macro is what strands them there, and
20920/// the walk indexes exactly what this reads, so admitting the region is safe
20921/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
20922/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
20923/// the whole class body.
20924fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
20925    if node.kind() != "ERROR" {
20926        return false;
20927    }
20928    let run = stranded_declaration_run(node, source);
20929    run.complete && !run.declarations.is_empty()
20930}
20931
20932/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
20933/// translation-unit-shaped class-body reparse as an assignment expression:
20934/// `Ctor() = default`. Admit only the exact assignment shape, and only when
20935/// the same reparse retains a separate constructor declarator for that name.
20936/// The second condition keeps an arbitrary expression statement from becoming
20937/// a member merely because it contains a callee followed by `default`.
20938/// A constructor declaration a statement-context class-body reparse reads as
20939/// a bare call statement (`value(pn_data_t* d);`). At statement scope a
20940/// constructor declaration is indistinguishable from a call, but a direct
20941/// child of the reparse root whose callee names the class can only be the
20942/// constructor: a member with no return type is nothing else.
20943fn cpp_reparsed_bare_constructor_call<'tree>(
20944    node: Node<'tree>,
20945    class_name: &str,
20946    source: &str,
20947) -> Option<Node<'tree>> {
20948    if node.kind() != "expression_statement" {
20949        return None;
20950    }
20951    let call = node
20952        .named_child(0)
20953        .filter(|child| child.kind() == "call_expression")?;
20954    let function = call.child_by_field_name("function")?;
20955    (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
20956        .then_some(call)
20957}
20958
20959fn cpp_reparsed_defaulted_constructor_is_indexable(
20960    node: Node<'_>,
20961    members: &[Node<'_>],
20962    class_name: Option<&str>,
20963    source: &str,
20964) -> bool {
20965    let Some(class_name) = class_name else {
20966        return false;
20967    };
20968    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
20969        return false;
20970    }
20971    let Some(assignment) = node.named_child(0) else {
20972        return false;
20973    };
20974    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
20975        return false;
20976    }
20977    if assignment
20978        .child(1)
20979        .is_none_or(|operator| operator.kind() != "=")
20980    {
20981        return false;
20982    }
20983    let Some(call) = assignment.child_by_field_name("left") else {
20984        return false;
20985    };
20986    let Some(default_value) = assignment.child_by_field_name("right") else {
20987        return false;
20988    };
20989    let Some(function) = call.child_by_field_name("function") else {
20990        return false;
20991    };
20992    let Some(arguments) = call.child_by_field_name("arguments") else {
20993        return false;
20994    };
20995    if call.kind() != "call_expression"
20996        || function.kind() != "identifier"
20997        || arguments.kind() != "argument_list"
20998        || arguments.named_child_count() != 0
20999        || default_value.kind() != "identifier"
21000        || node_text(default_value, source) != "default"
21001        || node_text(function, source) != class_name
21002    {
21003        return false;
21004    }
21005    members.iter().any(|member| {
21006        if same_node(*member, node) {
21007            return false;
21008        }
21009        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21010    })
21011}
21012
21013/// Return the constructor name from one direct class-member-shaped reparse
21014/// item. The function-declarator in an `ERROR` is admitted only through the
21015/// existing ERROR-only member proof; clean function definitions must carry no
21016/// return type and must retain either a body or ABI 15's `default_method_clause`.
21017fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21018    let function_declarator = match node.kind() {
21019        "function_definition"
21020            if !node.has_error() && node.child_by_field_name("type").is_none() =>
21021        {
21022            let has_body = node
21023                .child_by_field_name("body")
21024                .is_some_and(|body| body.kind() == "compound_statement");
21025            let has_default_method_clause = node
21026                .named_children(&mut node.walk())
21027                .any(|child| child.kind() == "default_method_clause");
21028            if !has_body && !has_default_method_clause {
21029                return None;
21030            }
21031            node.child_by_field_name("declarator")
21032                .and_then(extract_function_declarator)
21033        }
21034        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21035            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21036            let mut candidate = None;
21037            while let Some(current) = stack.pop() {
21038                if current.kind() == "function_declarator" {
21039                    if candidate.is_some() {
21040                        return None;
21041                    }
21042                    candidate = Some(current);
21043                    continue;
21044                }
21045                if current.kind() == "ERROR" {
21046                    let mut cursor = current.walk();
21047                    stack.extend(current.named_children(&mut cursor));
21048                }
21049            }
21050            candidate
21051        }
21052        _ => None,
21053    }?;
21054    cpp_function_declarator_name_node(function_declarator)
21055        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21056}
21057
21058fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21059    cpp_reparsed_members_are_indexable_for_class(root, source, None)
21060}
21061
21062fn cpp_reparsed_members_are_indexable_for_class(
21063    root: Node<'_>,
21064    source: &str,
21065    class_name: Option<&str>,
21066) -> bool {
21067    let mut cursor = root.walk();
21068    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
21069    let mut saw_member = false;
21070    let mut index = 0;
21071    while index < children.len() {
21072        let child = children[index];
21073        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21074            saw_member = true;
21075            index += 3;
21076            continue;
21077        }
21078        if let Some(recovered) = fragmented_class_body(child, source) {
21079            let Some(tree) = cpp_reparse_fragmented_class_body(
21080                source,
21081                recovered.body.reparse_start,
21082                recovered.body.reparse_end,
21083            ) else {
21084                return false;
21085            };
21086            if !cpp_reparsed_members_are_indexable_for_class(
21087                tree.root_node(),
21088                source,
21089                Some(recovered.name.as_str()),
21090            ) {
21091                return false;
21092            }
21093            saw_member = true;
21094            index += 1;
21095            while index < children.len()
21096                && children[index].end_byte() <= recovered.body.class_range.end_byte
21097            {
21098                index += 1;
21099            }
21100            continue;
21101        }
21102        match child.kind() {
21103            "comment" => {}
21104            "labeled_statement" => saw_member = true,
21105            "function_definition" => {
21106                if child.has_error()
21107                    && !cpp_reparsed_member_function_is_indexable(child, source)
21108                    && cpp_sentinel_macro_region(child, source).is_none()
21109                {
21110                    return false;
21111                }
21112                saw_member = true;
21113            }
21114            // The attribute a string-argument macro leaves as a call statement
21115            // of its own. It declares nothing; the member it decorated is the
21116            // sibling after it, checked on its own turn.
21117            "expression_statement" if is_string_attribute_macro_statement(child) => {}
21118            "expression_statement"
21119                if cpp_reparsed_defaulted_constructor_is_indexable(
21120                    child, &children, class_name, source,
21121                ) =>
21122            {
21123                saw_member = true;
21124            }
21125            "expression_statement"
21126                if class_name.is_some_and(|class_name| {
21127                    cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21128                }) =>
21129            {
21130                saw_member = true;
21131            }
21132            "ERROR"
21133                if (cpp_reparsed_member_error_is_indexable(child)
21134                    || cpp_reparsed_adjacent_copy_control_error(child, source)
21135                    || cpp_reparsed_stranded_member_error(child, source))
21136                    && (child
21137                        .next_named_sibling()
21138                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21139                        || cpp_reparsed_member_error_with_preprocessed_body(child)
21140                        // The macro-declaration grammar folds the member after
21141                        // the copy-control pair into the same ERROR and leaves
21142                        // its body as the following sibling.
21143                        || (cpp_reparsed_adjacent_copy_control_error(child, source)
21144                            && child
21145                                .next_named_sibling()
21146                                .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21147            {
21148                saw_member = true;
21149            }
21150            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21151                saw_member = true;
21152            }
21153            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21154                saw_member = true;
21155            }
21156            "expression_statement"
21157                if cpp_is_stray_semicolon(child, source)
21158                    && child.prev_named_sibling().is_some_and(|error| {
21159                        cpp_reparsed_member_error_is_indexable(error)
21160                            || cpp_reparsed_adjacent_copy_control_error(error, source)
21161                            || cpp_reparsed_stranded_member_error(error, source)
21162                    }) =>
21163            {
21164                saw_member = true;
21165            }
21166            "compound_statement"
21167                if cpp_reparsed_constructor_body_is_indexable(child, source)
21168                    || cpp_reparsed_attribute_requires_body(child, source)
21169                    || child.prev_named_sibling().is_some_and(|error| {
21170                        cpp_reparsed_adjacent_copy_control_error(error, source)
21171                    }) =>
21172            {
21173                saw_member = true;
21174            }
21175            // A copy-control declaration the macro-declaration grammar reads
21176            // as a function-like macro wrapping a type: `XMLElement( const
21177            // XMLElement& )` in a statement-context reparse is a
21178            // `macro_type_specifier`, admitted only where the parse would
21179            // otherwise contain an error. The live `;` beside it is the
21180            // declaration's own terminator, the same proof the ERROR
21181            // envelope's stray semicolon carried.
21182            "macro_type_specifier"
21183                if child
21184                    .child_by_field_name("name")
21185                    .is_some_and(|name| name.kind() == "identifier")
21186                    && child
21187                        .child_by_field_name("type")
21188                        .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21189                    && child
21190                        .next_sibling()
21191                        .is_some_and(|sibling| sibling.kind() == ";" && !sibling.is_missing()) =>
21192            {
21193                saw_member = true;
21194            }
21195            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21196            _ => return false,
21197        }
21198        index += 1;
21199    }
21200    saw_member
21201}
21202
21203/// Detect the malformed constructor shape that tree-sitter exposes as an
21204/// access-label statement followed by initializer-looking declarations. The
21205/// declarations are not class members: visiting their `location(loc)` and
21206/// `string(s)` function declarators would publish synthetic functions. The
21207/// export-class fallback keeps the original sibling nodes and therefore avoids
21208/// this parser artifact. The returned range identifies the real constructor
21209/// header, which can be reparsed independently as a structured declarator.
21210fn cpp_reparsed_synthetic_initializer_constructor_range(
21211    root: Node<'_>,
21212    class_name: &str,
21213    source: &str,
21214    constructor_end: usize,
21215) -> Option<std::ops::Range<usize>> {
21216    let mut stack = {
21217        let mut cursor = root.walk();
21218        root.named_children(&mut cursor).collect::<Vec<_>>()
21219    };
21220    while let Some(current) = stack.pop() {
21221        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21222            current,
21223            class_name,
21224            source,
21225            constructor_end,
21226        ) {
21227            return Some(range);
21228        }
21229        if current.kind() == "ERROR" {
21230            let mut cursor = current.walk();
21231            stack.extend(current.named_children(&mut cursor));
21232        }
21233    }
21234    None
21235}
21236
21237/// Recover an inline constructor that a function-like export macro makes
21238/// tree-sitter merge with the following overload. In the reparsed class-body
21239/// region, the access label wraps one declaration whose ERROR contains the
21240/// constructor declarator and its base-initializer/body, while the declaration's
21241/// ordinary declarator is the following overload. Every boundary below comes
21242/// from that CST; no source syntax is reparsed by hand.
21243fn cpp_reparsed_merged_inline_constructor<'tree>(
21244    root: Node<'tree>,
21245    class_name: &str,
21246    source: &str,
21247) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21248    let mut stack = vec![root];
21249    while let Some(current) = stack.pop() {
21250        if current.kind() != "labeled_statement" {
21251            let mut cursor = current.walk();
21252            stack.extend(current.named_children(&mut cursor));
21253            continue;
21254        }
21255        let declaration = current
21256            .named_children(&mut current.walk())
21257            .find(|child| child.kind() == "declaration")?;
21258        if declaration
21259            .child_by_field_name("type")
21260            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21261        {
21262            continue;
21263        }
21264        let following = declaration
21265            .child_by_field_name("declarator")
21266            .and_then(extract_function_declarator)
21267            .and_then(cpp_function_declarator_name_node);
21268        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21269            continue;
21270        }
21271        let mut declaration_cursor = declaration.walk();
21272        let Some(error) = declaration
21273            .named_children(&mut declaration_cursor)
21274            .find(|child| child.kind() == "ERROR")
21275        else {
21276            continue;
21277        };
21278        let mut error_cursor = error.walk();
21279        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21280        let Some(constructor) = error_children.iter().copied().find(|child| {
21281            child.kind() == "function_declarator"
21282                && cpp_function_declarator_name_node(*child)
21283                    .is_some_and(|name| node_text(name, source).trim() == class_name)
21284        }) else {
21285            continue;
21286        };
21287        let Some(body) = error_children.iter().copied().find_map(|child| {
21288            (child.kind() == "init_declarator")
21289                .then(|| child.child_by_field_name("value"))
21290                .flatten()
21291                .filter(|value| value.kind() == "initializer_list")
21292        }) else {
21293            continue;
21294        };
21295        if constructor.end_byte() > body.start_byte() {
21296            continue;
21297        }
21298        return Some((constructor.start_byte()..body.end_byte(), body));
21299    }
21300    None
21301}
21302
21303fn cpp_reparsed_synthetic_initializer_constructor(
21304    node: Node<'_>,
21305    class_name: &str,
21306    source: &str,
21307    constructor_end: usize,
21308) -> Option<std::ops::Range<usize>> {
21309    if node.kind() != "labeled_statement" {
21310        return None;
21311    }
21312    let mut cursor = node.walk();
21313    let named = node
21314        .named_children(&mut cursor)
21315        .filter(|child| child.kind() != "comment")
21316        .collect::<Vec<_>>();
21317    let label = named.first()?;
21318    if label.kind() != "statement_identifier"
21319        || !matches!(
21320            node_text(*label, source).trim(),
21321            "public" | "private" | "protected"
21322        )
21323    {
21324        return None;
21325    }
21326    let call_error_index = named.iter().position(|child| {
21327        if child.kind() != "ERROR" {
21328            return false;
21329        }
21330        // The macro-declaration grammar keeps the constructor head's type as
21331        // the ERROR's own type field; the older grammar flattened the head
21332        // into a call expression inside it. ERROR nodes retain field labels
21333        // only by child index in runtime 0.25.
21334        let type_child = (0..child.child_count()).find_map(|index| {
21335            let candidate = child.child(index)?;
21336            (child.field_name_for_child(index as u32) == Some("type")
21337                && candidate.kind() == "type_identifier"
21338                && node_text(candidate, source).trim() == class_name)
21339                .then_some(candidate)
21340        });
21341        if type_child.is_some() {
21342            return true;
21343        }
21344        let mut stack = vec![*child];
21345        while let Some(current) = stack.pop() {
21346            if current.kind() == "call_expression"
21347                && current
21348                    .child_by_field_name("function")
21349                    .is_some_and(|function| {
21350                        function.kind() == "identifier"
21351                            && node_text(function, source).trim() == class_name
21352                    })
21353            {
21354                return true;
21355            }
21356            let mut cursor = current.walk();
21357            stack.extend(current.named_children(&mut cursor));
21358        }
21359        false
21360    })?;
21361    let constructor_start = {
21362        let head = named[call_error_index];
21363        let type_child = (0..head.child_count()).find_map(|index| {
21364            let candidate = head.child(index)?;
21365            (head.field_name_for_child(index as u32) == Some("type")
21366                && candidate.kind() == "type_identifier"
21367                && node_text(candidate, source).trim() == class_name)
21368                .then_some(candidate)
21369        });
21370        if let Some(type_node) = type_child {
21371            type_node.start_byte()
21372        } else {
21373            let mut stack = vec![head];
21374            let mut found = None;
21375            while let Some(current) = stack.pop() {
21376                if current.kind() == "call_expression"
21377                    && current
21378                        .child_by_field_name("function")
21379                        .is_some_and(|function| {
21380                            function.kind() == "identifier"
21381                                && node_text(function, source).trim() == class_name
21382                        })
21383                {
21384                    found = Some(current);
21385                    break;
21386                }
21387                let mut cursor = current.walk();
21388                stack.extend(current.named_children(&mut cursor));
21389            }
21390            found?.start_byte()
21391        }
21392    };
21393    named.iter().skip(call_error_index + 1).find(|child| {
21394        child.kind() == "declaration" && child.has_error() && {
21395            let mut cursor = child.walk();
21396            child.named_children(&mut cursor).any(|declarator| {
21397                declarator.kind() == "init_declarator"
21398                    && declarator
21399                        .child_by_field_name("declarator")
21400                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
21401                    && declarator
21402                        .child_by_field_name("value")
21403                        .is_some_and(|value| value.kind() == "initializer_list")
21404            })
21405        }
21406    })?;
21407    Some(constructor_start..constructor_end)
21408}
21409
21410fn cpp_reparsed_exact_constructor_declarator<'tree>(
21411    root: Node<'tree>,
21412    start: usize,
21413    class_name: &str,
21414    source: &str,
21415) -> Option<Node<'tree>> {
21416    let mut candidate = None;
21417    let mut stack = vec![root];
21418    while let Some(current) = stack.pop() {
21419        if current.kind() == "function_declarator"
21420            && current.start_byte() == start
21421            && cpp_function_declarator_name_node(current)
21422                .is_some_and(|name| node_text(name, source).trim() == class_name)
21423        {
21424            if candidate.is_some() {
21425                return None;
21426            }
21427            candidate = Some(current);
21428            continue;
21429        }
21430        let mut cursor = current.walk();
21431        stack.extend(current.named_children(&mut cursor));
21432    }
21433    candidate
21434}
21435
21436fn cpp_is_indexable_item_kind(kind: &str) -> bool {
21437    matches!(
21438        kind,
21439        "namespace_definition"
21440            | "class_specifier"
21441            | "struct_specifier"
21442            | "union_specifier"
21443            | "enum_specifier"
21444            | "function_definition"
21445            | "template_declaration"
21446            | "declaration"
21447            | "field_declaration"
21448            | "alias_declaration"
21449            | "static_assert_declaration"
21450            | "type_definition"
21451            | "using_declaration"
21452            | "linkage_specification"
21453            | "preproc_def"
21454            | "preproc_function_def"
21455            | "preproc_include"
21456            | "preproc_if"
21457            | "preproc_ifdef"
21458            | "preproc_call"
21459    )
21460}
21461
21462#[cfg(test)]
21463mod tests {
21464    use super::*;
21465    use crate::adapter::parse_cpp_file;
21466    use brokk_bifrost_core::analyzer::parsed_file::{
21467        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
21468    };
21469    use brokk_bifrost_test_macros::hardwon;
21470    use std::fmt::Write;
21471
21472    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
21473        let tree = crate::diagnostics::parse_cpp_tree(source);
21474        let file = ProjectFile::new(std::env::temp_dir(), name);
21475        parse_cpp_file(&file, source, &tree)
21476    }
21477
21478    #[test]
21479    fn native_must_use_attribute_nodes_are_structural() {
21480        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
21481        let tree = crate::diagnostics::parse_cpp_tree(source);
21482        let root = tree.root_node();
21483        let mut declarations = Vec::new();
21484        walk_named_tree_preorder(root, true, |node| {
21485            if matches!(
21486                node.kind(),
21487                "declaration" | "function_definition" | "field_declaration"
21488            ) {
21489                declarations.push(node);
21490            }
21491            WalkControl::Continue
21492        });
21493        assert_eq!(
21494            declarations
21495                .into_iter()
21496                .map(|declaration| {
21497                    cpp_declaration_has_native_must_use_attribute(declaration, source)
21498                })
21499                .collect::<Vec<_>>(),
21500            vec![true, false, true]
21501        );
21502        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
21503        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
21504        let mut enum_specifier = None;
21505        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
21506            if node.kind() == "enum_specifier" {
21507                enum_specifier = Some(node);
21508                WalkControl::Break
21509            } else {
21510                WalkControl::Continue
21511            }
21512        });
21513        assert!(cpp_declaration_has_native_must_use_attribute(
21514            enum_specifier.expect("enum declaration"),
21515            enum_source
21516        ));
21517    }
21518
21519    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
21520        let mut rows = parsed
21521            .signature_metadata
21522            .iter()
21523            .flat_map(|(unit, entries)| {
21524                entries.iter().map(move |metadata| {
21525                    assert!(
21526                        metadata.callable_modifiers_recorded(),
21527                        "{} must record that the walk read its declaration shape",
21528                        unit.fq_name()
21529                    );
21530                    format!(
21531                        "{} decl_only={} static={} constructor={} visibility={:?}",
21532                        unit.fq_name(),
21533                        metadata.is_declaration_only(),
21534                        metadata.callable_is_static(),
21535                        metadata.callable_is_constructor(),
21536                        metadata
21537                            .callable_declared_visibility()
21538                            .expect("a recorded modifier set states a visibility"),
21539                    )
21540                })
21541            })
21542            .collect::<Vec<_>>();
21543        rows.sort();
21544        rows
21545    }
21546
21547    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
21548    /// exactly the spelling it was given, with the nested-class `$` recorded as
21549    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
21550    #[test]
21551    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
21552    fn rendered_name_fq_round_trips_nested_class_spellings() {
21553        assert!(cpp_rendered_name_fq("").is_empty());
21554        for rendered in [
21555            "Only",
21556            "Top$Only",
21557            "ns.Outer$Inner",
21558            "Top$Only.b",
21559            "ns.Outer$Inner.a",
21560            "Buck$Stop",
21561        ] {
21562            let fq = cpp_rendered_name_fq(rendered);
21563            assert_eq!(
21564                fq.display_native(Language::Cpp, segment_interner()),
21565                rendered,
21566                "native round trip for {rendered}"
21567            );
21568        }
21569        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
21570            .segments()
21571            .iter()
21572            .map(|segment| segment_interner().resolve(*segment).1)
21573            .collect();
21574        assert_eq!(
21575            kinds,
21576            [
21577                SegmentKind::Unknown,
21578                SegmentKind::Unknown,
21579                SegmentKind::Nested
21580            ]
21581        );
21582    }
21583
21584    /// #3631: a `::`-only source spelling must name the nested `$` identity
21585    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
21586    /// the single rendered-name parse.
21587    #[test]
21588    #[hardwon(
21589        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
21590    )]
21591    fn colon_source_aliases_name_nested_and_namespace_identities() {
21592        let native = |rendered: &str| {
21593            cpp_colon_source_aliases(rendered)
21594                .into_iter()
21595                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
21596                .collect::<Vec<_>>()
21597        };
21598        let top = native("Top::Only");
21599        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
21600        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
21601        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
21602        let nested = native("ns::Outer::Inner");
21603        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
21604        assert!(
21605            nested.contains(&"ns::Outer.Inner".to_string()),
21606            "{nested:?}"
21607        );
21608        let member = native("Top::Only::b");
21609        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
21610        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
21611        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
21612        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
21613        assert!(cpp_colon_source_aliases("Only").is_empty());
21614        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
21615            fq.segments()
21616                .iter()
21617                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
21618        }));
21619    }
21620
21621    #[test]
21622    #[hardwon(
21623        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
21624    )]
21625    fn displaced_terminator_ignores_nested_initializer_endif() {
21626        let source = "#ifdef ENABLE_ITEMS\n\
21627struct Item { int reg; };\n\
21628static const struct Item items[] = {{1}};\n\
21629static const struct Item extras[] = {\n\
21630  {0},\n\
21631#ifndef REDUCED\n\
21632  {1},\n\
21633#endif\n\
21634};\n\
21635int read_item(int i) { return items[i].reg; }\n\
21636#endif\n";
21637        let mut parser = tree_sitter::Parser::new();
21638        parser
21639            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21640            .expect("C++ grammar");
21641        let tree = parser.parse(source, None).expect("fixture tree");
21642        let conditional = tree
21643            .root_node()
21644            .named_child(0)
21645            .filter(|node| node.kind() == "preproc_ifdef")
21646            .expect("outer conditional");
21647
21648        assert!(conditional.has_error());
21649        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
21650        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
21651    }
21652
21653    #[test]
21654    #[hardwon(
21655        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21656    )]
21657    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
21658        let source = "struct Image { PyObject_HEAD Imaging image; };";
21659        let parsed = parse_cpp_declarations(source, "image.h");
21660        let names = parsed
21661            .declarations()
21662            .iter()
21663            .map(|unit| unit.fq_name())
21664            .collect::<Vec<_>>();
21665
21666        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
21667        assert!(
21668            names.iter().all(|name| name != "Image.Imaging"),
21669            "the pseudo-declarator must not become a field: {names:#?}"
21670        );
21671
21672        let pointer = parse_cpp_declarations(
21673            "struct Image { PyObject_HEAD Imaging *image; };",
21674            "image-pointer.h",
21675        );
21676        let pointer_names = pointer
21677            .declarations()
21678            .iter()
21679            .map(|unit| unit.fq_name())
21680            .collect::<Vec<_>>();
21681        assert!(
21682            pointer_names.iter().any(|name| name == "Image.image"),
21683            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
21684        );
21685        assert!(
21686            pointer_names.iter().all(|name| name != "Image.Imaging"),
21687            "the pointer recovery error must not become a field: {pointer_names:#?}"
21688        );
21689
21690        let unrelated_macro = parse_cpp_declarations(
21691            "struct Image { OTHER_HEAD Imaging other; };",
21692            "image-near-miss.h",
21693        );
21694        let unrelated_names = unrelated_macro
21695            .declarations()
21696            .iter()
21697            .map(|unit| unit.fq_name())
21698            .collect::<Vec<_>>();
21699        assert!(
21700            unrelated_names.iter().any(|name| name == "Image.other"),
21701            "an object-like macro in specifier position is a `macro_modifier`, so the \
21702             member is the structured best-effort reading, the same one the \
21703             macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
21704        );
21705        assert!(
21706            unrelated_names
21707                .iter()
21708                .all(|name| name != "Image.OTHER_HEAD"),
21709            "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
21710        );
21711    }
21712
21713    #[test]
21714    #[hardwon(
21715        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
21716    )]
21717    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
21718        let source = r#"namespace testing {
21719namespace internal {
21720    namespace detail {
21721        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
21722        public:
21723            int value() const { return count_ + 1; }
21724        private:
21725            int count_;
21726        };
21727        class GTEST_API_ [[nodiscard]] OtherReporter {
21728        public:
21729            int value() const { return count_ + 2; }
21730        private:
21731            int count_;
21732        };
21733    }
21734
21735    template <typename T>
21736    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
21737
21738    class TailReporter {};
21739}
21740}
21741"#;
21742        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
21743        let declarations = parsed.declarations();
21744        let mut parser = tree_sitter::Parser::new();
21745        parser
21746            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21747            .unwrap();
21748        let tree = parser.parse(source, None).unwrap();
21749        let tail_start = source.find("TailReporter").expect("tail class");
21750        let tail_node = tree
21751            .root_node()
21752            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
21753            .expect("tail class AST node");
21754        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
21755        assert!(
21756            tree.root_node().has_error(),
21757            "the malformed class must exercise recovery"
21758        );
21759        assert!(index.region_at(tail_start).is_some());
21760        assert_eq!(
21761            index.enclosing_namespace_components(tail_node, source),
21762            ["testing", "internal"]
21763        );
21764        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
21765        let mut recovered_parsed = ParsedFile::new(String::new());
21766        let class_unit = CodeUnit::new_fq(
21767            file.clone(),
21768            CodeUnitType::Class,
21769            "testing",
21770            "ScopedFakeTestPartResultReporter",
21771            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
21772        );
21773        let scope = ScopeInfo {
21774            package_name: "testing".to_string(),
21775            module: None,
21776            class_unit: Some(class_unit),
21777            template_signature: Some("<typename T>".to_string()),
21778            template_metadata: Some(CppTemplateMetadata {
21779                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
21780                primary_fq_name: String::new(),
21781                parameters: Vec::new(),
21782                specialization_arguments: Vec::new(),
21783                alias_target: None,
21784            }),
21785            declarations_are_fields: true,
21786            recovered_specialization_member_scope: true,
21787            visible_using_namespaces: Vec::new(),
21788        };
21789        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
21790        let properties = RefCell::new(HashMap::default());
21791        let dependencies =
21792            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
21793        let ancestry = ParentIndex::new(tree.root_node());
21794        let mut visitor = CppVisitor {
21795            file: &file,
21796            source,
21797            parsed: &mut recovered_parsed,
21798            source_collector: &collector,
21799            source_properties: &properties,
21800            source_dependencies: &dependencies,
21801            primary_ancestry: &ancestry,
21802            primary_root_id: tree.root_node().id(),
21803            source_links: HashMap::default(),
21804            source_declarations: HashSet::default(),
21805            preprocessor_context_cache: Default::default(),
21806            active_source_context: None,
21807            c_tag_semantics: false,
21808            c_tag_scope_witness: false,
21809            recovered_class_sibling_scopes: HashMap::default(),
21810            consumed_fragment_regions: Vec::new(),
21811            orphaned_namespaces: &index,
21812            partitioned_regions: Vec::new(),
21813            namespace_forward_scans: HashMap::default(),
21814            field_owners: None,
21815            recovery_captures: Vec::new(),
21816            object_macro_fields: HashMap::default(),
21817            ambiguous_object_macro_fields: HashSet::default(),
21818        };
21819        let recovered = visitor
21820            .recovered_namespace_scope(tail_node, &scope)
21821            .expect("the tail must use the stolen namespace scope");
21822        assert_eq!(recovered.package_name, "testing::internal");
21823        assert!(
21824            recovered.class_unit.is_none(),
21825            "recovered namespace declarations cannot retain the malformed class owner"
21826        );
21827        assert!(recovered.template_signature.is_none());
21828        assert!(recovered.template_metadata.is_none());
21829        assert!(!recovered.declarations_are_fields);
21830        assert!(!recovered.recovered_specialization_member_scope);
21831        assert!(
21832            declarations
21833                .iter()
21834                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
21835            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
21836        );
21837        assert!(
21838            declarations
21839                .iter()
21840                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
21841            "the recovered free function remains in its namespace: {declarations:#?}"
21842        );
21843        assert!(
21844            declarations
21845                .iter()
21846                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
21847            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
21848        );
21849        assert!(
21850            declarations.iter().all(|unit| {
21851                !unit
21852                    .short_name()
21853                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
21854            }),
21855            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
21856        );
21857        assert!(
21858            declarations
21859                .iter()
21860                .all(|unit| !unit.identifier().is_empty()),
21861            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
21862        );
21863    }
21864
21865    /// #4292: a recovered namespace level whose close the brace stack could not
21866    /// prove keeps its head-only range, and the first declaration the walk
21867    /// reaches inside the region is not the namespace. The Module's name
21868    /// occurrence must be the level's own name token; deriving it from the
21869    /// walk's current node paired the head range with an unrelated name and
21870    /// panicked the collector's containment assertion.
21871    #[test]
21872    fn recovered_namespace_levels_name_their_own_name_tokens() {
21873        // #3084's collapsed-head fixture without the namespace's own close:
21874        // the file ends inside the namespace, so the brace stack proves no
21875        // close for its `{` and the level keeps its `namespace Name {` head
21876        // alone (#3328).
21877        let source = r#"namespace outer::app {
21878
21879    class Target {
21880        int value_;
21881    };
21882
21883    template <typename T>
21884    class Holder {
21885    public:
21886        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
21887
21888#define HOLDER_DEFINE_OP( id, op )                                             \
21889    template <typename U>                                                      \
21890    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
21891        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
21892        return Target{};                                                       \
21893    }
21894
21895        HOLDER_DEFINE_OP( equal, == )
21896#undef HOLDER_DEFINE_OP
21897        T m_lhs;
21898    };
21899
21900    class Tail {};
21901"#;
21902        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
21903        let mut parser = tree_sitter::Parser::new();
21904        parser
21905            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21906            .unwrap();
21907        let tree = parser.parse(source, None).unwrap();
21908        assert!(
21909            tree.root_node().has_error(),
21910            "the fixture must reproduce the parse damage"
21911        );
21912        let target_start = source.find("class Target").expect("target class");
21913        let target_node = tree
21914            .root_node()
21915            .named_descendant_for_byte_range(target_start, target_start + "class".len())
21916            .expect("target class AST node");
21917        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
21918        // The macro-declaration grammar keeps the namespace head and its
21919        // members inside a surviving `namespace_definition`; the missing close
21920        // no longer collapses the head into an ERROR, so the scope authority
21921        // answers from parsed ancestry rather than a recovered region.
21922        assert_eq!(
21923            index.enclosing_namespace_components(target_node, source),
21924            ["outer", "app"]
21925        );
21926        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
21927        let mut recovered_parsed = ParsedFile::new(String::new());
21928        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
21929        let properties = RefCell::new(HashMap::default());
21930        let dependencies =
21931            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
21932        let ancestry = ParentIndex::new(tree.root_node());
21933        let mut visitor = CppVisitor {
21934            file: &file,
21935            source,
21936            parsed: &mut recovered_parsed,
21937            source_collector: &collector,
21938            source_properties: &properties,
21939            source_dependencies: &dependencies,
21940            primary_ancestry: &ancestry,
21941            primary_root_id: tree.root_node().id(),
21942            source_links: HashMap::default(),
21943            source_declarations: HashSet::default(),
21944            preprocessor_context_cache: Default::default(),
21945            active_source_context: None,
21946            c_tag_semantics: false,
21947            c_tag_scope_witness: false,
21948            recovered_class_sibling_scopes: HashMap::default(),
21949            consumed_fragment_regions: Vec::new(),
21950            orphaned_namespaces: &index,
21951            partitioned_regions: Vec::new(),
21952            namespace_forward_scans: HashMap::default(),
21953            field_owners: None,
21954            recovery_captures: Vec::new(),
21955            object_macro_fields: HashMap::default(),
21956            ambiguous_object_macro_fields: HashSet::default(),
21957        };
21958        // The surviving `namespace_definition` takes the ordinary namespace
21959        // visit, which declares each level with its own name token.
21960        visitor.visit_container(
21961            tree.root_node(),
21962            &ancestry,
21963            "",
21964            None,
21965            None,
21966            None,
21967            Vec::new(),
21968        );
21969        let collector = collector.borrow();
21970        for (module_name, expected_name) in [
21971            (
21972                "outer",
21973                Range {
21974                    start_byte: 10,
21975                    end_byte: 15,
21976                    start_line: 1,
21977                    end_line: 1,
21978                },
21979            ),
21980            (
21981                "outer::app",
21982                Range {
21983                    start_byte: 17,
21984                    end_byte: 20,
21985                    start_line: 1,
21986                    end_line: 1,
21987                },
21988            ),
21989        ] {
21990            let declaration = visitor
21991                .source_links
21992                .iter()
21993                .find(|(unit, _)| unit.fq_name() == module_name)
21994                .expect("the recovered module links a source declaration")
21995                .1
21996                .last()
21997                .expect("the recovered module declaration");
21998            let declaration = collector.declaration(*declaration);
21999            let name = declaration
22000                .name
22001                .expect("the recovered module records its name occurrence");
22002            assert_eq!(
22003                collector.occurrence(name).range,
22004                expected_name,
22005                "the recovered module names its own token: {module_name}"
22006            );
22007        }
22008        assert!(
22009            parsed.declarations().iter().any(|unit| {
22010                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
22011            }),
22012            "the recovered namespace members keep their namespace: {:#?}",
22013            parsed.declarations()
22014        );
22015    }
22016
22017    #[test]
22018    #[hardwon(
22019        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22020    )]
22021    fn object_like_field_macros_materialize_owner_specific_declarations() {
22022        let source = r#"#define PUBLIC_FIELDS int public_value;
22023#define PRIVATE_FIELDS int private_value;
22024#define NOT_A_FIELD_LIST not a declaration
22025
22026struct First {
22027  PUBLIC_FIELDS
22028  PRIVATE_FIELDS
22029};
22030struct Second {
22031  PUBLIC_FIELDS
22032  NOT_A_FIELD_LIST
22033};
22034#undef PUBLIC_FIELDS
22035struct Third {
22036  PUBLIC_FIELDS
22037};
22038"#;
22039        let parsed = parse_cpp_declarations(source, "macro-fields.c");
22040        let fields = parsed
22041            .declarations()
22042            .iter()
22043            .filter(|unit| unit.is_field())
22044            .map(|unit| unit.fq_name())
22045            .collect::<Vec<_>>();
22046
22047        assert!(
22048            fields.contains(&"First.public_value".to_string()),
22049            "{fields:?}"
22050        );
22051        assert!(
22052            fields.contains(&"First.private_value".to_string()),
22053            "{fields:?}"
22054        );
22055        assert!(
22056            fields.contains(&"Second.public_value".to_string()),
22057            "{fields:?}"
22058        );
22059        assert!(
22060            !fields.iter().any(|field| field.contains("not_a_field")),
22061            "malformed macro must fail closed: {fields:?}"
22062        );
22063        assert!(
22064            !fields.iter().any(|field| field.starts_with("Third.")),
22065            "undefined macro must fail closed: {fields:?}"
22066        );
22067    }
22068
22069    #[test]
22070    fn conditional_macro_redefinition_is_ambiguous() {
22071        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";
22072        let tree = crate::diagnostics::parse_cpp_tree(source);
22073        let fields = collect_cpp_object_macro_fields(tree.root_node(), source);
22074        assert!(
22075            !fields.contains_key("FIELDS"),
22076            "conditional macro state must not select one branch's declaration: {fields:?}"
22077        );
22078    }
22079
22080    #[test]
22081    #[hardwon(
22082        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22083    )]
22084    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
22085        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
22086        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
22087        let mut macros = parsed
22088            .declarations()
22089            .iter()
22090            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
22091            .collect::<Vec<_>>();
22092        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
22093
22094        assert_eq!(macros.len(), 2, "{macros:#?}");
22095        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
22096        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
22097        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
22098        assert_eq!(
22099            parsed.declaration_ranges(macros[1])[0].start_byte,
22100            source.rfind("#define VALUE 2").expect("second definition")
22101        );
22102    }
22103
22104    #[test]
22105    #[hardwon(
22106        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
22107    )]
22108    fn identifies_export_macro_class_base_displaced_into_declarator() {
22109        let source = r#"#define PROJECT_API_
22110namespace project {
22111namespace internal {
22112template <typename T>
22113class Base {};
22114}
22115template <typename T>
22116class Wrapper;
22117template <>
22118class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
22119}
22120"#;
22121        let mut parser = tree_sitter::Parser::new();
22122        parser
22123            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22124            .unwrap();
22125        let tree = parser.parse(source, None).unwrap();
22126        let start = source.find("internal::Base<int>").expect("base");
22127        let mut base = tree
22128            .root_node()
22129            .descendant_for_byte_range(start, start + 8)
22130            .expect("base syntax");
22131        while base.kind() != "qualified_identifier" {
22132            base = base.parent().expect("qualified base ancestor");
22133        }
22134        assert!(
22135            is_recovered_exported_class_base_type_node(base, source),
22136            "{}",
22137            tree.root_node().to_sexp()
22138        );
22139    }
22140
22141    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
22142        parsed
22143            .declarations()
22144            .iter()
22145            .filter(|unit| unit.is_function())
22146            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
22147            .collect()
22148    }
22149
22150    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
22151        let mut identities = all_function_identities(parsed)
22152            .into_iter()
22153            .map(|(name, signature)| (name, signature.unwrap_or_default()))
22154            .collect::<Vec<_>>();
22155        identities.sort();
22156        identities
22157    }
22158
22159    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
22160    /// prototype macros. Each case is parsed independently so a preceding
22161    /// malformed line cannot flatten the next declaration's fields into one
22162    /// cascading `ERROR`; that shape has no structural proof and must fail
22163    /// closed rather than fall back to a token scan. Parameter names are
22164    /// dropped from signatures just as they are for ordinary C prototypes.
22165    #[test]
22166    #[hardwon(
22167        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
22168    )]
22169    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
22170        let cases = [
22171            (
22172                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
22173                "pg_typemap_fit_to_result",
22174                "(VALUE, VALUE)",
22175            ),
22176            (
22177                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
22178                "pg_typemap_result_value",
22179                "(t_typemap *, VALUE, int, int)",
22180            ),
22181            (
22182                "void pg_typemap_mark _(( void * ));",
22183                "pg_typemap_mark",
22184                "(void *)",
22185            ),
22186            (
22187                "void init_pg_type_map _(( void ));",
22188                "init_pg_type_map",
22189                "(void)",
22190            ),
22191            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
22192            (
22193                "extern VALUE pg_extern _(( VALUE, VALUE ));",
22194                "pg_extern",
22195                "(VALUE, VALUE)",
22196            ),
22197            (
22198                "size_t pg_typemap_memsize _(( const void * ));",
22199                "pg_typemap_memsize",
22200                "(const void *)",
22201            ),
22202            (
22203                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
22204                "pg_wrap_socket_io",
22205                "(int, VALUE, VALUE *, int *)",
22206            ),
22207            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
22208            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
22209            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
22210        ];
22211
22212        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
22213            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
22214            assert_eq!(
22215                function_identities(&parsed),
22216                vec![(name.to_string(), signature.to_string())],
22217                "{source}: {:#?}",
22218                parsed.declarations()
22219            );
22220            assert!(
22221                parsed.declarations().iter().all(|unit| !unit.is_field()),
22222                "{source} must not retain the malformed field: {:#?}",
22223                parsed.declarations()
22224            );
22225        }
22226    }
22227
22228    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
22229    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
22230    /// it as a multiplication of a type by a call
22231    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
22232    /// wrapped in an `expression_statement` that `has_error()` only because
22233    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
22234    /// here with a clean preceding field so the prototype remains the
22235    /// parser-owned `expression_statement` this recovery requires.
22236    #[test]
22237    #[hardwon(
22238        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
22239    )]
22240    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
22241        let cases = [
22242            (
22243                "PGconn *pg_get_pgconn _(( VALUE ));",
22244                "pg_get_pgconn",
22245                "(VALUE)",
22246            ),
22247            (
22248                "PGresult* pgresult_get _(( VALUE ));",
22249                "pgresult_get",
22250                "(VALUE)",
22251            ),
22252        ];
22253        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
22254            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
22255            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
22256            assert_eq!(
22257                function_identities(&parsed),
22258                vec![(name.to_string(), signature.to_string())],
22259                "{source}: {:#?}",
22260                parsed.declarations()
22261            );
22262            assert_eq!(
22263                parsed
22264                    .declarations()
22265                    .iter()
22266                    .filter(|unit| unit.is_field())
22267                    .map(|unit| unit.fq_name())
22268                    .collect::<Vec<_>>(),
22269                vec!["rb_mPG".to_string()],
22270                "{source}: {:#?}",
22271                parsed.declarations()
22272            );
22273        }
22274    }
22275
22276    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
22277    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
22278    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
22279    /// regions. The witnessed declaration must still become a Function and
22280    /// its type must not survive as the name of a bogus Field. This test does
22281    /// not claim that structurally flattened neighboring prototypes are safe
22282    /// to recover.
22283    #[test]
22284    #[hardwon(
22285        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
22286    )]
22287    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
22288        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
22289VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
22290int pg_typemap_fit_to_copy_get                         _(( VALUE ));
22291VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
22292t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
22293VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
22294void pg_typemap_mark                                   _(( void * ));
22295size_t pg_typemap_memsize                              _(( const void * ));
22296void pg_typemap_compact                                _(( void * ));
22297
22298PGconn *pg_get_pgconn                                  _(( VALUE ));
22299t_pg_connection *pg_get_connection                     _(( VALUE ));
22300VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
22301#ifdef __GNUC__
22302__attribute__((format(printf, 3, 4)))
22303#endif
22304NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
22305VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
22306void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
22307
22308
22309VALUE pg_new_result                                    _(( PGresult *, VALUE ));
22310VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
22311PGresult* pgresult_get                                 _(( VALUE ));
22312VALUE pg_result_check                                  _(( VALUE ));
22313VALUE pg_result_clear                                  _(( VALUE ));
22314VALUE pg_tuple_new                                     _(( VALUE, int ));
22315
22316/*
22317 * Fetch the data pointer for the result object
22318 */
22319static inline t_pg_result *
22320pgresult_get_this( VALUE self )
22321{
22322	return RTYPEDDATA_DATA(self);
22323}
22324
22325
22326rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
22327const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
22328rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
22329
22330"#;
22331        let parsed = parse_cpp_declarations(source, "pg.h");
22332        assert!(
22333            function_identities(&parsed)
22334                .iter()
22335                .any(|(name, signature)| name == "pg_typemap_result_value"
22336                    && signature == "(t_typemap *, VALUE, int, int)"),
22337            "the real issue witness must be a Function with its C signature: {:#?}",
22338            parsed.declarations()
22339        );
22340        assert!(
22341            parsed
22342                .declarations()
22343                .iter()
22344                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
22345            "the issue witness must not leave its return type as a Field name: {:#?}",
22346            parsed.declarations()
22347        );
22348    }
22349
22350    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
22351    /// between `explicit` and a constructor name onto the name, producing a
22352    /// `qualified_identifier` whose `::` it had to invent. The declared member
22353    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
22354    /// macro in type position, was already recovered and is the control that
22355    /// both spellings agree.
22356    #[test]
22357    #[hardwon(
22358        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
22359    )]
22360    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
22361        let source = r#"class SIMD_4x26 final {
22362   public:
22363      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
22364      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
22365      int m_v;
22366};
22367"#;
22368        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
22369        assert_eq!(
22370            function_identities(&parsed),
22371            vec![
22372                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
22373                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
22374            ],
22375            "{:#?}",
22376            parsed.declarations()
22377        );
22378    }
22379
22380    #[test]
22381    #[hardwon(
22382        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
22383    )]
22384    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
22385        let source = r#"struct cpu_spec {};
22386static struct cpu_spec __initdata cpu_specs[];
22387static struct cpu_spec ns::cpu_specs[];
22388"#;
22389        let tree = crate::diagnostics::parse_cpp_tree(source);
22390        let root = tree.root_node();
22391        let declarations = root
22392            .named_children(&mut root.walk())
22393            .filter(|node| node.kind() == "declaration")
22394            .collect::<Vec<_>>();
22395        let names = declarations
22396            .iter()
22397            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
22398            .filter_map(|declarator| extract_variable_name(declarator, source))
22399            .collect::<Vec<_>>();
22400        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
22401    }
22402
22403    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
22404    /// `ERROR` holding the wrapped declaration and every declaration after it
22405    /// until the parser recovers. All of them must be indexed, each with the
22406    /// byte range that spells it.
22407    #[test]
22408    #[hardwon(
22409        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
22410    )]
22411    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
22412        let source = r#"#include <cstdint>
22413struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
22414    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
22415                     struct llama_model * model,
22416              struct llama_context_params   params),
22417            "use llama_init_from_model instead");
22418    LLAMA_API int32_t llama_tokenize(
22419        const struct llama_vocab * vocab,
22420                      const char * text,
22421                            bool   parse_special);
22422    LLAMA_API int32_t llama_other(int a);
22423"#;
22424        let parsed = parse_cpp_declarations(source, "llama.h");
22425        assert_eq!(
22426            function_identities(&parsed),
22427            vec![
22428                (
22429                    "llama_new_context_with_model".to_string(),
22430                    "(struct llama_model *, struct llama_context_params)".to_string()
22431                ),
22432                ("llama_other".to_string(), "(int)".to_string()),
22433                (
22434                    "llama_tokenize".to_string(),
22435                    "(const struct llama_vocab *, const char *, bool)".to_string()
22436                ),
22437            ],
22438            "{:#?}",
22439            parsed.declarations()
22440        );
22441
22442        // Each recovered declaration owns the source that spells it, so
22443        // navigation lands on the declaration and not on the macro envelope.
22444        for (name, expected) in [
22445            (
22446                "llama_new_context_with_model",
22447                "LLAMA_API struct llama_context * llama_new_context_with_model(",
22448            ),
22449            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
22450            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
22451        ] {
22452            let unit = parsed
22453                .declarations()
22454                .iter()
22455                .find(|unit| unit.is_function() && unit.fq_name() == name)
22456                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
22457            let [range] = parsed.declaration_ranges(unit) else {
22458                panic!("{name} must have exactly one range");
22459            };
22460            let text = &source[range.start_byte..range.end_byte];
22461            assert!(
22462                text.starts_with(expected),
22463                "{name} range is {text:?}, expected it to start with {expected:?}"
22464            );
22465            assert!(
22466                text.ends_with(')') || text.ends_with(';'),
22467                "{name}: {text:?}"
22468            );
22469        }
22470    }
22471
22472    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
22473    /// invocation's `(` at the end of its line. The macro-declaration grammar
22474    /// keeps each invocation in its own declaration-scope `ERROR`: the wrapped
22475    /// declaration and the hint's exploded words sit in the parameter list,
22476    /// the invocation's `)` closes that list, and its `;` is left to the
22477    /// sibling `expression_statement`. (Before the macro-declaration grammar,
22478    /// the second invocation's envelope swallowed the third into a run of bare
22479    /// siblings; #3094 read that flattening from the token order, and the
22480    /// collapsed-run recovery still owns the envelope/statement pairing the
22481    /// same way.)
22482    ///
22483    /// The identifiers are `library_*` at whisper's own lengths on purpose:
22484    /// tree-sitter prices error recovery partly by how much input a repair
22485    /// skips, and the same construct written with shorter names parses cleanly.
22486    #[test]
22487    #[hardwon(
22488        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
22489    )]
22490    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
22491        let source = r#"    LIBRARY_DEPRECATED(
22492        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
22493        "use library_init_from_file_with_params instead"
22494    );
22495    LIBRARY_DEPRECATED(
22496        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
22497        "use library_init_from_buffer_with_params instead"
22498    );
22499    LIBRARY_DEPRECATED(
22500        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
22501        "use library_init_with_params instead"
22502    );
22503"#;
22504        let mut parser = tree_sitter::Parser::new();
22505        parser
22506            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22507            .expect("C++ grammar");
22508        let tree = parser.parse(source, None).expect("C++ tree");
22509        let root = tree.root_node();
22510
22511        // The shape this recovery exists for: three sibling `ERROR` envelopes,
22512        // each holding one whole invocation, each followed by the statement
22513        // that owns the invocation's `;`.
22514        let mut cursor = root.walk();
22515        let items = root.children(&mut cursor).collect::<Vec<_>>();
22516        let [
22517            first,
22518            first_statement,
22519            second,
22520            second_statement,
22521            third,
22522            third_statement,
22523        ] = items.as_slice()
22524        else {
22525            panic!("{}", root.to_sexp());
22526        };
22527        let envelopes = [
22528            (first, first_statement),
22529            (second, second_statement),
22530            (third, third_statement),
22531        ];
22532        for (envelope, statement) in envelopes {
22533            assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
22534            assert_eq!(
22535                statement.kind(),
22536                "expression_statement",
22537                "{}",
22538                root.to_sexp()
22539            );
22540            assert!(
22541                node_text(*envelope, source).ends_with(')'),
22542                "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
22543                root.to_sexp()
22544            );
22545        }
22546
22547        // Each is admitted, and each names the byte just past its own `;`. A
22548        // self-contained invocation swallows nothing, so the recovery owns
22549        // only its own bytes.
22550        let ancestry = ParentIndex::new(root);
22551        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
22552            .expect("the first invocation");
22553        assert_eq!(
22554            &source[..first_run.invocation_end],
22555            &source[..source.find("instead\"\n    );").expect("first hint")
22556                + "instead\"\n    );".len()]
22557        );
22558        for (envelope, _) in envelopes {
22559            let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
22560                .expect("each invocation");
22561            assert_eq!(
22562                run.region_end, run.invocation_end,
22563                "a self-contained invocation owns only its own bytes"
22564            );
22565        }
22566
22567        let parsed = parse_cpp_declarations(source, "library.h");
22568        assert_eq!(
22569            function_identities(&parsed),
22570            vec![
22571                (
22572                    "library_init".to_string(),
22573                    "(struct library_model_loader *)".to_string()
22574                ),
22575                (
22576                    "library_init_from_buffer".to_string(),
22577                    "(void *, size_t)".to_string()
22578                ),
22579                (
22580                    "library_init_from_file".to_string(),
22581                    "(const char *)".to_string()
22582                ),
22583            ],
22584            "{:#?}",
22585            parsed.declarations()
22586        );
22587    }
22588
22589    #[test]
22590    #[hardwon(
22591        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
22592    )]
22593    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
22594        let source = r#"void host() {
22595#ifdef ENABLED
22596LIBRARY_DEPRECATED(
22597    int hidden(const char* value),
22598    "hint"
22599);
22600#endif
22601}
22602"#;
22603        let mut parser = tree_sitter::Parser::new();
22604        parser
22605            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22606            .expect("C++ grammar");
22607        let tree = parser.parse(source, None).expect("C++ tree");
22608        let root = tree.root_node();
22609        let function = root.named_child(0).expect("ordinary function");
22610        let body = function
22611            .child_by_field_name("body")
22612            .expect("ordinary function body");
22613        let conditional = body.named_child(0).expect("conditional body");
22614        let mut conditional_cursor = conditional.walk();
22615        let envelope = conditional
22616            .named_children(&mut conditional_cursor)
22617            .find(|child| child.kind() == "ERROR")
22618            .expect("macro error");
22619        assert_eq!(conditional.kind(), "preproc_ifdef");
22620        assert_eq!(envelope.kind(), "ERROR");
22621
22622        let ancestry = ParentIndex::new(root);
22623        assert!(declaration_scope_container(envelope, &ancestry).is_none());
22624        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
22625
22626        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
22627        assert!(
22628            parsed
22629                .declarations()
22630                .iter()
22631                .all(|unit| unit.fq_name() != "hidden"),
22632            "a function-body macro error must not become a global declaration: {:#?}",
22633            parsed.declarations()
22634        );
22635    }
22636
22637    /// The hand-off that keeps the scan finite. An invocation that reaches the
22638    /// end of its own declaration scope swallowed nothing, and the ordinary
22639    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
22640    /// that region for every invocation it finds, so admitting it here would
22641    /// hand the scan its own input back.
22642    #[test]
22643    #[hardwon(
22644        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
22645    )]
22646    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
22647        let source = r#"LIBRARY_DEPRECATED(
22648        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
22649        "use library_init_from_file_with_params instead"
22650    );"#;
22651        let mut parser = tree_sitter::Parser::new();
22652        parser
22653            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22654            .expect("C++ grammar");
22655        let tree = parser.parse(source, None).expect("C++ tree");
22656        let root = tree.root_node();
22657        let head = root.named_child(0).expect("the invocation");
22658        let ancestry = ParentIndex::new(root);
22659        assert!(
22660            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
22661            "{}",
22662            root.to_sexp()
22663        );
22664        assert!(
22665            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
22666            "the ordinary reader must be the one that has it: {}",
22667            root.to_sexp()
22668        );
22669        // The index is a speed substitution, never a semantic one: the
22670        // unindexed path is `Node::parent` itself and must agree.
22671        let unindexed = ParentIndex::unindexed();
22672        assert!(
22673            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
22674            "indexed and unindexed climbs must agree"
22675        );
22676        assert_eq!(
22677            macro_wrapped_declarations(head, source, &ancestry).len(),
22678            macro_wrapped_declarations(head, source, &unindexed).len(),
22679            "indexed and unindexed climbs must agree"
22680        );
22681    }
22682
22683    /// Negative controls for the same recovery: a macro invocation whose
22684    /// arguments are not a declaration recovers nothing, whether the parser
22685    /// keeps it clean, reads the arguments as a type, or reads them as a bare
22686    /// declarator.
22687    #[test]
22688    #[hardwon(
22689        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
22690    )]
22691    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
22692        for source in [
22693            "int before;\nFOO(1, 2);\nint after;\n",
22694            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
22695            "int before;\nMACRO(int a, int b);\nint after;\n",
22696            "DECLARE_HANDLE(HWND);\nint after;\n",
22697        ] {
22698            let parsed = parse_cpp_declarations(source, "macro-call.h");
22699            assert_eq!(
22700                function_identities(&parsed),
22701                Vec::new(),
22702                "{source:?} must declare no function: {:#?}",
22703                parsed.declarations()
22704            );
22705        }
22706    }
22707
22708    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
22709    /// makes tree-sitter read the macro as the member's type and its argument
22710    /// list as a parenthesized declarator, which then swallows the attributed
22711    /// member and the member written after it. Both are members.
22712    #[test]
22713    #[hardwon(
22714        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
22715    )]
22716    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
22717        let source = r#"#include <string_view>
22718namespace Botan {
22719class DL_Group final {
22720   public:
22721      DL_Group() = default;
22722      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
22723      DL_Group(std::string_view pem, int format);
22724      size_t get_p() const;
22725};
22726}
22727"#;
22728        let parsed = parse_cpp_declarations(source, "dl_group.h");
22729        assert_eq!(
22730            function_identities(&parsed),
22731            vec![
22732                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
22733                (
22734                    "Botan.DL_Group.DL_Group".to_string(),
22735                    "(std::string_view)".to_string()
22736                ),
22737                (
22738                    "Botan.DL_Group.DL_Group".to_string(),
22739                    "(std::string_view, int)".to_string()
22740                ),
22741                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
22742            ],
22743            "{:#?}",
22744            parsed.declarations()
22745        );
22746    }
22747
22748    /// #2552 shape 3, export-macro class. The class head macro makes the body a
22749    /// `compound_statement`, so members come from the region reparse, and one
22750    /// string-attribute member used to make that reparse unindexable -- which
22751    /// left the class with no members at all.
22752    #[test]
22753    #[hardwon(
22754        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
22755    )]
22756    fn an_export_macro_class_keeps_its_string_attribute_members() {
22757        let source = r#"#include <string_view>
22758namespace Botan {
22759class BOTAN_PUBLIC_API(2, 0) DL_Group final {
22760   public:
22761      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
22762      DL_Group(std::string_view pem, int format);
22763      size_t get_p() const;
22764};
22765}
22766"#;
22767        let parsed = parse_cpp_declarations(source, "dl_group.h");
22768        assert!(
22769            parsed
22770                .declarations()
22771                .iter()
22772                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
22773            "{:#?}",
22774            parsed.declarations()
22775        );
22776        assert_eq!(
22777            function_identities(&parsed),
22778            vec![
22779                (
22780                    "Botan.DL_Group.DL_Group".to_string(),
22781                    "(std::string_view)".to_string()
22782                ),
22783                (
22784                    "Botan.DL_Group.DL_Group".to_string(),
22785                    "(std::string_view, int)".to_string()
22786                ),
22787                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
22788            ],
22789            "{:#?}",
22790            parsed.declarations()
22791        );
22792    }
22793
22794    /// #2552 shape 3, the `= default` variant plus the access-label
22795    /// constructor. The attributed defaulted constructor separates cleanly, but
22796    /// it strands the next member in a bare `ERROR`, and a constructor written
22797    /// with a member-initializer list under `private:` dissolves into
22798    /// expression soup that the reparse recovers from its own source range.
22799    #[test]
22800    #[hardwon(
22801        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
22802    )]
22803    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
22804        let source = r#"namespace Botan {
22805class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
22806   public:
22807      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
22808      XMSS_Parameters(int oid, int len);
22809      size_t len() const;
22810
22811   private:
22812      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
22813            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
22814
22815      int m_oid;
22816      int m_wots_oid;
22817      size_t m_element_size;
22818      size_t m_tree_height;
22819};
22820}
22821"#;
22822        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
22823        let constructors = function_identities(&parsed)
22824            .into_iter()
22825            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
22826            .map(|(_, signature)| signature)
22827            .collect::<Vec<_>>();
22828        assert_eq!(
22829            constructors,
22830            vec![
22831                "()".to_string(),
22832                "(int, int)".to_string(),
22833                "(int, int, size_t, size_t)".to_string(),
22834            ],
22835            "{:#?}",
22836            parsed.declarations()
22837        );
22838    }
22839
22840    /// Negative control for the same rule: a real qualified name spells its
22841    /// `::` in the source, so the separator is present rather than MISSING and
22842    /// the out-of-line definition keeps its owner.
22843    #[test]
22844    #[hardwon(
22845        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
22846    )]
22847    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
22848        let source = r#"namespace shell {
22849struct Outer {
22850   struct Inner {
22851      Inner(int v);
22852      void run(int v);
22853   };
22854};
22855Outer::Inner::Inner(int v) {}
22856void Outer::Inner::run(int v) {}
22857}
22858"#;
22859        let parsed = parse_cpp_declarations(source, "outer.cpp");
22860        let names = function_identities(&parsed)
22861            .into_iter()
22862            .map(|(fq_name, _)| fq_name)
22863            .collect::<Vec<_>>();
22864        assert!(
22865            names
22866                .iter()
22867                .all(|name| name.starts_with("shell.Outer$Inner.")),
22868            "{names:#?}"
22869        );
22870    }
22871
22872    #[test]
22873    #[hardwon(
22874        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
22875    )]
22876    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
22877        let source = r#"namespace control {
22878template <typename T>
22879class AnySpan;
22880template <typename T>
22881class ABSL_ATTRIBUTE_VIEW AnySpan {
22882 public:
22883  int begin() const;
22884};
22885}
22886
22887namespace absl {
22888ABSL_NAMESPACE_BEGIN
22889template <typename T>
22890class ABSL_ATTRIBUTE_VIEW Span {
22891 public:
22892  int begin() const;
22893  int back() const;
22894};
22895
22896int begin();
22897int back();
22898}
22899"#;
22900        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
22901        let declarations = parsed.declarations();
22902        assert!(
22903            declarations
22904                .iter()
22905                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
22906        );
22907        for method in ["begin", "back"] {
22908            assert!(declarations.iter().any(|unit| {
22909                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
22910            }));
22911            assert!(
22912                declarations.iter().any(|unit| {
22913                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
22914                })
22915            );
22916        }
22917        assert!(
22918            declarations
22919                .iter()
22920                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
22921        );
22922        assert!(
22923            declarations
22924                .iter()
22925                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
22926        );
22927        assert!(
22928            declarations
22929                .iter()
22930                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
22931        );
22932    }
22933
22934    #[test]
22935    #[hardwon(
22936        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22937    )]
22938    fn explicit_global_member_definition_has_canonical_package_boundary() {
22939        let source = r#"
22940namespace arangodb::aql {
22941class ExecutionPlan {
22942 public:
22943  template<class... Args> Node* createNode(Args&&... args);
22944};
22945}
22946
22947template<class... Args>
22948Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
22949"#;
22950        let parsed = parse_cpp_declarations(source, "global-member.cpp");
22951
22952        assert!(parsed.declarations().iter().any(|unit| {
22953            unit.is_function()
22954                && unit.package_name() == "arangodb::aql"
22955                && unit.short_name() == "ExecutionPlan.createNode"
22956                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
22957        }));
22958    }
22959
22960    #[test]
22961    #[hardwon(
22962        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22963    )]
22964    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
22965        let source = r#"
22966#ifndef TINYXML2_INCLUDED
22967#define TINYXML2_INCLUDED
22968namespace tinyxml2 {
22969class TINYXML2_LIB XMLUtil {
22970 public:
22971  static const char* SkipWhiteSpace(const char* p) {
22972    while (*p) {
22973      if (*p == ' ') {
22974        ++p;
22975      }
22976    }
22977    return p;
22978  }
22979  static bool StringEqual(const char* p, const char* q) {
22980    return p == q;
22981  }
22982  class TINYXML2_LIB Helper {
22983   public:
22984    void Touch();
22985  };
22986  static void ToStr(int value, char* buffer);
22987 private:
22988  static const char* writeBoolTrue;
22989};
22990
22991class TINYXML2_LIB XMLNode {
22992 public:
22993  virtual XMLNode* ShallowClone() const = 0;
22994  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
22995};
22996}
22997#endif
22998"#;
22999        let mut parser = tree_sitter::Parser::new();
23000        parser
23001            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23002            .unwrap();
23003        let tree = parser.parse(source, None).unwrap();
23004        let mut boundary_found = false;
23005        walk_named_tree_preorder(tree.root_node(), true, |node| {
23006            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
23007                && name == "XMLUtil"
23008            {
23009                boundary_found = fragmented_export_sibling_class_boundary(
23010                    node,
23011                    source,
23012                    &ParentIndex::unindexed(),
23013                )
23014                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
23015                .is_some_and(|(_, name, _, _)| name == "XMLNode");
23016            }
23017            WalkControl::Continue
23018        });
23019        assert!(
23020            boundary_found,
23021            "fixture must exercise the recovered sibling boundary"
23022        );
23023
23024        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
23025        assert!(
23026            parsed
23027                .declarations()
23028                .iter()
23029                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
23030            "{:#?}",
23031            parsed.declarations()
23032        );
23033        assert!(
23034            parsed
23035                .declarations()
23036                .iter()
23037                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
23038            "{:#?}",
23039            parsed.declarations()
23040        );
23041        assert!(parsed.declarations().iter().any(|unit| {
23042            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
23043        }));
23044        assert!(
23045            parsed
23046                .declarations()
23047                .iter()
23048                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
23049        );
23050        assert!(
23051            parsed
23052                .declarations()
23053                .iter()
23054                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
23055        );
23056    }
23057
23058    #[test]
23059    #[hardwon(
23060        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23061    )]
23062    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
23063        // Clang's diagnostic suite intentionally contains this ill-formed
23064        // spelling. The analyzer must retain the parser's explicit-global AST
23065        // boundary instead of constructing `cwg311::::cwg311::X`.
23066        let parsed = parse_cpp_declarations(
23067            r#"
23068namespace cwg311 {
23069namespace X { namespace Y {} }
23070namespace ::cwg311::X {}
23071}
23072"#,
23073            "explicit-global-namespace.cpp",
23074        );
23075
23076        assert!(parsed.declarations().iter().any(|unit| {
23077            unit.kind() == CodeUnitType::Module
23078                && unit.short_name() == "cwg311::X"
23079                && unit.fq_name() == "cwg311::X"
23080        }));
23081        assert!(
23082            parsed
23083                .declarations()
23084                .iter()
23085                .all(|unit| !unit.short_name().contains("::::")),
23086            "recovered namespace names must not retain empty scope components: {:#?}",
23087            parsed.declarations()
23088        );
23089    }
23090
23091    #[test]
23092    #[hardwon(
23093        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23094    )]
23095    fn repeated_scope_separator_does_not_create_empty_function_owner() {
23096        let scope = ScopeInfo {
23097            package_name: "X".to_string(),
23098            module: None,
23099            class_unit: None,
23100            template_signature: None,
23101            template_metadata: None,
23102            declarations_are_fields: false,
23103            recovered_specialization_member_scope: false,
23104            visible_using_namespaces: Vec::new(),
23105        };
23106
23107        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
23108
23109        assert!(owner.is_none());
23110        assert_eq!(name, "doit");
23111        assert_eq!(package, "X");
23112    }
23113
23114    #[test]
23115    #[hardwon(
23116        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23117    )]
23118    fn trailing_decltype_expression_is_not_a_function_declarator() {
23119        let source = r#"
23120namespace boost { namespace detail {
23121#if ! defined(BOOST_NO_SFINAE_EXPR) && \
23122    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
23123    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
23124#define BOOST_THREAD_PROVIDES_INVOKE
23125#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
23126template <class Fp, class A0, class ...Args>
23127inline auto
23128invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
23129       BOOST_THREAD_RV_REF(Args) ...args)
23130    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
23131{
23132    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
23133}
23134#endif
23135#endif
23136}}
23137"#;
23138        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
23139
23140        assert!(
23141            parsed
23142                .declarations()
23143                .iter()
23144                .all(|unit| unit.short_name() != ".*f")
23145        );
23146    }
23147
23148    fn find_class_named<'tree>(
23149        root: Node<'tree>,
23150        source: &str,
23151        expected_name: &str,
23152    ) -> Option<Node<'tree>> {
23153        let mut stack = vec![root];
23154        while let Some(node) = stack.pop() {
23155            if node.kind() == "class_specifier"
23156                && node
23157                    .child_by_field_name("name")
23158                    .is_some_and(|name| node_text(name, source) == expected_name)
23159            {
23160                return Some(node);
23161            }
23162            let mut cursor = node.walk();
23163            stack.extend(node.named_children(&mut cursor));
23164        }
23165        None
23166    }
23167
23168    #[test]
23169    #[hardwon(
23170        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23171    )]
23172    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
23173        let source = r#"EXPORT void definition(struct Value value) {}
23174EXPORT void prototype(struct Value value);
23175"#;
23176        let mut parser = tree_sitter::Parser::new();
23177        parser
23178            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23179            .unwrap();
23180        let tree = parser.parse(source, None).unwrap();
23181        let root = tree.root_node();
23182        let mut cursor = root.walk();
23183        let callables = root
23184            .named_children(&mut cursor)
23185            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
23186            .collect::<Vec<_>>();
23187
23188        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
23189        for callable in callables {
23190            // The macro-declaration grammar parses these cleanly, with the
23191            // export macro retained as a `macro_modifier`; the preserved
23192            // callable and its `struct` parameter are what the sentinel gate
23193            // rejects on, with or without an ERROR in the envelope.
23194            assert!(
23195                cpp_sentinel_macro_parts(callable, source).is_none(),
23196                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
23197            );
23198        }
23199    }
23200
23201    #[test]
23202    #[hardwon(
23203        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23204    )]
23205    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
23206        let source = r#"namespace absl {
23207ABSL_NAMESPACE_BEGIN
23208// Generate a floating-point variate conforming to a Beta distribution:
23209template <typename RealType = double>
23210class beta_distribution {
23211 public:
23212  using result_type = RealType;
23213
23214
23215  beta_distribution() : beta_distribution(1) {}
23216
23217  explicit beta_distribution(result_type alpha, result_type beta = 1)
23218      : param_(alpha, beta) {}
23219
23220  explicit beta_distribution(const param_type& p) : param_(p) {}
23221
23222  void reset() {}
23223
23224  // Generating functions
23225  template <typename URBG>
23226  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
23227    return (*this)(g, param_);
23228  }
23229
23230};
23231ABSL_NAMESPACE_END
23232}  // namespace absl
23233"#;
23234        let mut parser = tree_sitter::Parser::new();
23235        parser
23236            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23237            .unwrap();
23238        let tree = parser.parse(source, None).unwrap();
23239        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
23240        let body = namespace
23241            .child_by_field_name("body")
23242            .expect("fixture namespace body");
23243        let sentinel = body.named_child(0).expect("sentinel envelope");
23244
23245        // The macro-declaration grammar keeps the sentinel head in an `ERROR`
23246        // envelope that ends at the class name, with the class body as the
23247        // sibling it recovered with; the bogus callable the older grammar
23248        // assembled from the last member is no longer part of the envelope.
23249        assert_eq!(sentinel.kind(), "ERROR");
23250        assert!(
23251            cpp_sentinel_macro_parts(sentinel, source).is_some(),
23252            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
23253        );
23254
23255        let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
23256        for expected in [
23257            "absl.beta_distribution",
23258            "absl.beta_distribution.beta_distribution",
23259            "absl.beta_distribution.reset",
23260        ] {
23261            assert!(
23262                parsed
23263                    .declarations()
23264                    .iter()
23265                    .any(|unit| unit.fq_name() == expected),
23266                "missing recovered declaration {expected}: {:#?}",
23267                parsed.declarations()
23268            );
23269        }
23270    }
23271
23272    #[test]
23273    #[hardwon(
23274        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23275    )]
23276    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
23277        let source = r#"namespace absl {
23278ABSL_NAMESPACE_BEGIN
23279// absl::discrete_distribution
23280//
23281// A discrete distribution produces random integers i, where 0 <= i < n
23282template <typename IntType = int>
23283class discrete_distribution {
23284 public:
23285  using result_type = IntType;
23286  class param_type {
23287   public:
23288    param_type() { init(); }
23289    template <typename InputIterator>
23290    explicit param_type(InputIterator begin, InputIterator end)
23291        : p_(begin, end) {
23292      init();
23293    }
23294  };
23295  discrete_distribution() : param_() {}
23296  explicit discrete_distribution(const param_type& p) : param_(p) {}
23297};
23298ABSL_NAMESPACE_END
23299}  // namespace absl
23300"#;
23301        let mut parser = tree_sitter::Parser::new();
23302        parser
23303            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23304            .unwrap();
23305        let tree = parser.parse(source, None).unwrap();
23306        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
23307        let body = namespace
23308            .child_by_field_name("body")
23309            .expect("fixture namespace body");
23310        let sentinel = body.named_child(0).expect("sentinel envelope");
23311        let callable = sentinel
23312            .child_by_field_name("declarator")
23313            .and_then(extract_function_declarator)
23314            .and_then(cpp_function_declarator_name_node)
23315            .expect("preserved callable name");
23316
23317        assert_eq!(sentinel.kind(), "function_definition");
23318        assert_eq!(callable.kind(), "identifier");
23319        assert!(
23320            cpp_sentinel_macro_parts(sentinel, source).is_some(),
23321            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
23322        );
23323    }
23324
23325    #[test]
23326    #[hardwon(
23327        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23328    )]
23329    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
23330        let source = r#"
23331#define CPPCHECKLIB
23332class Library {
23333    struct Container {
23334        CPPCHECKLIB static std::string toString(Yield yield);
23335        CPPCHECKLIB static std::string toString(Action action);
23336    };
23337};
23338"#;
23339        let mut parser = tree_sitter::Parser::new();
23340        parser
23341            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23342            .unwrap();
23343        let tree = parser.parse(source, None).unwrap();
23344        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
23345        let parsed = parse_cpp_file(&file, source, &tree);
23346        assert!(
23347            parsed
23348                .declarations()
23349                .iter()
23350                .all(|unit| unit.fq_name() != "Library$Container.std"),
23351            "the qualified return-type namespace must not become a field: {:#?}",
23352            parsed.declarations()
23353        );
23354        for expected in ["(Yield)", "(Action)"] {
23355            assert!(
23356                parsed.declarations().iter().any(|unit| {
23357                    unit.is_function()
23358                        && unit.fq_name() == "Library$Container.toString"
23359                        && unit.signature() == Some(expected)
23360                }),
23361                "recovered toString overload {expected} is missing: {:#?}",
23362                parsed.declarations()
23363            );
23364        }
23365    }
23366
23367    #[test]
23368    #[hardwon(
23369        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23370    )]
23371    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
23372        let source = r#"
23373#define SIMPLECPP_LIB
23374namespace simplecpp {
23375using TokenString = std::string;
23376struct Location { int line{}; };
23377class SIMPLECPP_LIB Token {
23378  TokenString prefix;
23379  void prefix_method() {}
23380 public:
23381  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
23382      whitespaceahead(wsahead), location(loc), string(s)
23383      // The comment must not hide the constructor body from recovery.
23384      {
23385      flags();
23386  }
23387  TokenString string;
23388  bool whitespaceahead;
23389  Location location;
23390  Token *previous{};
23391 private:
23392  void flags() {
23393      whitespaceahead = true;
23394  }
23395};
23396}
23397"#;
23398        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
23399
23400        let location_fields = parsed
23401            .declarations()
23402            .iter()
23403            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
23404            .collect::<Vec<_>>();
23405        assert_eq!(
23406            location_fields.len(),
23407            1,
23408            "location should have one class-owned declaration: {:#?}",
23409            parsed.declarations()
23410        );
23411        assert!(
23412            location_fields[0].is_field(),
23413            "location has wrong kind: {:#?}",
23414            parsed.declarations()
23415        );
23416        assert!(
23417            parsed.declarations().iter().all(|unit| {
23418                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
23419            })
23420        );
23421        assert!(
23422            parsed.declarations().iter().all(|unit| {
23423                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
23424            })
23425        );
23426        assert!(
23427            parsed
23428                .declarations()
23429                .iter()
23430                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
23431        );
23432        assert!(
23433            parsed
23434                .declarations()
23435                .iter()
23436                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
23437            "the recovered class must retain its constructor: {:#?}",
23438            parsed.declarations()
23439        );
23440        assert!(
23441            parsed
23442                .declarations()
23443                .iter()
23444                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
23445        );
23446        assert!(parsed.declarations().iter().any(|unit| {
23447            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
23448        }));
23449        let constructor = parsed
23450            .declarations()
23451            .iter()
23452            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
23453            .expect("recovered constructor");
23454        let constructor_start = source.find("Token(const").expect("constructor start");
23455        let constructor_end = source
23456            .get(
23457                ..source
23458                    .find("  TokenString string;")
23459                    .expect("constructor end"),
23460            )
23461            .expect("constructor slice")
23462            .trim_end()
23463            .len();
23464        assert!(
23465            parsed
23466                .navigation_ranges
23467                .get(constructor)
23468                .is_some_and(|ranges| {
23469                    ranges.iter().any(|range| {
23470                        range.start_byte == constructor_start && range.end_byte == constructor_end
23471                    })
23472                }),
23473            "constructor navigation must span the full body: {:#?}",
23474            parsed.navigation_ranges
23475        );
23476        assert_eq!(
23477            parsed
23478                .signature_metadata
23479                .get(constructor)
23480                .and_then(|metadata| metadata.first())
23481                .and_then(SignatureMetadata::callable_linkage),
23482            Some(CallableLinkage::External)
23483        );
23484        let token_class = parsed
23485            .declarations()
23486            .iter()
23487            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
23488            .expect("recovered Token class");
23489        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
23490        assert!(
23491            parsed
23492                .navigation_ranges
23493                .get(token_class)
23494                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
23495            "class navigation must include the terminating semicolon: {:#?}",
23496            parsed.navigation_ranges
23497        );
23498    }
23499
23500    #[test]
23501    #[hardwon(
23502        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23503    )]
23504    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
23505        let source = r#"
23506#define SIMPLECPP_LIB
23507namespace simplecpp {
23508using TokenString = std::string;
23509class Macro;
23510struct Location {
23511  unsigned int fileIndex{};
23512  unsigned int line{};
23513  unsigned int col{};
23514};
23515struct Output {
23516  int type;
23517};
23518class SIMPLECPP_LIB Token {
23519 public:
23520  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
23521      whitespaceahead(wsahead), location(loc), string(s) {
23522      flags();
23523  }
23524  Token(const Token &tok) :
23525      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
23526      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
23527      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
23528  Token &operator=(const Token &tok) = delete;
23529  const TokenString& str() const { return string; }
23530  void setstr(const std::string &s) { string = s; flags(); }
23531  bool isOneOf(const char ops[]) const;
23532  TokenString macro;
23533  char op;
23534  bool comment;
23535  bool name;
23536  bool number;
23537  bool whitespaceahead;
23538  Location location;
23539  Token *previous{};
23540  Token *next{};
23541 private:
23542  void flags() {
23543      name = !string.empty();
23544      comment = false;
23545      number = false;
23546      op = 0;
23547  }
23548  TokenString string;
23549};
23550}
23551struct Following {
23552  int type;
23553};
23554class SIMPLECPP_LIB Later {
23555 public:
23556  Later(int value) : value(value) {}
23557  int value;
23558};
23559"#;
23560        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
23561        assert!(
23562            parsed
23563                .declarations()
23564                .iter()
23565                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
23566        );
23567        assert!(
23568            !parsed
23569                .declarations()
23570                .iter()
23571                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
23572        );
23573        assert!(
23574            parsed
23575                .declarations()
23576                .iter()
23577                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
23578        );
23579        assert!(
23580            !parsed
23581                .declarations()
23582                .iter()
23583                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
23584        );
23585        assert!(
23586            parsed
23587                .declarations()
23588                .iter()
23589                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
23590        );
23591        assert!(
23592            parsed
23593                .declarations()
23594                .iter()
23595                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
23596        );
23597        assert!(
23598            parsed
23599                .declarations()
23600                .iter()
23601                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
23602        );
23603        assert!(
23604            parsed
23605                .declarations()
23606                .iter()
23607                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
23608        );
23609        assert!(
23610            parsed
23611                .declarations()
23612                .iter()
23613                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
23614        );
23615        assert!(
23616            parsed
23617                .declarations()
23618                .iter()
23619                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
23620        );
23621        assert!(parsed.declarations().iter().all(|unit| {
23622            !matches!(
23623                unit.fq_name().as_str(),
23624                "simplecpp.Token.Following" | "simplecpp.Token.Later"
23625            )
23626        }));
23627        assert!(
23628            !parsed
23629                .declarations()
23630                .iter()
23631                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
23632            "the following struct must remain outside the recovered Token class"
23633        );
23634    }
23635
23636    #[test]
23637    #[hardwon(
23638        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23639    )]
23640    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
23641        let source = r#"
23642#define SIMPLECPP_LIB
23643namespace {
23644namespace simplecpp {
23645using TokenString = std::string;
23646struct Location { int line{}; };
23647class SIMPLECPP_LIB HiddenToken {
23648 public:
23649  HiddenToken(const TokenString &s, const Location &loc) :
23650      location(loc), string(s) {
23651      flags();
23652  }
23653  TokenString string;
23654  Location location;
23655  HiddenToken *previous{};
23656 private:
23657  void flags() {}
23658};
23659}
23660}
23661"#;
23662        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
23663        let constructor = parsed
23664            .declarations()
23665            .iter()
23666            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
23667            .expect("recovered anonymous-namespace constructor");
23668        assert_eq!(
23669            parsed
23670                .signature_metadata
23671                .get(constructor)
23672                .and_then(|metadata| metadata.first())
23673                .and_then(SignatureMetadata::callable_linkage),
23674            Some(CallableLinkage::Internal)
23675        );
23676    }
23677
23678    #[test]
23679    #[hardwon(
23680        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23681    )]
23682    fn macro_qualified_static_field_keeps_real_declarator() {
23683        let source = r#"#define JSON_INLINE_VARIABLE
23684struct Reader {
23685static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
23686static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
23687static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
23688};"#;
23689        let mut parser = tree_sitter::Parser::new();
23690        parser
23691            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23692            .unwrap();
23693        let tree = parser.parse(source, None).unwrap();
23694        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
23695        let parsed = parse_cpp_file(&file, source, &tree);
23696        for expected in [
23697            "Reader.npos",
23698            "Reader.other",
23699            "Reader.pointer",
23700            "Reader.reference",
23701        ] {
23702            assert!(
23703                parsed
23704                    .declarations()
23705                    .iter()
23706                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
23707                "real macro-decorated field {expected} is missing: {:#?}",
23708                parsed.declarations()
23709            );
23710        }
23711        assert!(
23712            parsed
23713                .declarations()
23714                .iter()
23715                .all(|unit| unit.fq_name() != "Reader.std"),
23716            "qualified type prefix became a pseudo-field: {:#?}",
23717            parsed.declarations()
23718        );
23719        let root = tree.root_node();
23720        let mut stack = vec![root];
23721        let mut signatures = Vec::new();
23722        // The macro-declaration grammar keeps `JSON_INLINE_VARIABLE` as a
23723        // `macro_modifier`, so the fields arrive as ordinary clean field
23724        // declarations whose declarators carry the signature text the
23725        // bitfield-clause recovery used to reconstruct.
23726        while let Some(current) = stack.pop() {
23727            if current.kind() == "field_declaration" {
23728                let mut cursor = current.walk();
23729                for declarator in current.children_by_field_name("declarator", &mut cursor) {
23730                    signatures.push(render_cpp_field_signature(current, declarator, source));
23731                }
23732            }
23733            let mut cursor = current.walk();
23734            stack.extend(current.named_children(&mut cursor));
23735        }
23736        signatures.sort();
23737        assert_eq!(
23738            signatures,
23739            [
23740                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
23741                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
23742                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
23743                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
23744            ]
23745        );
23746    }
23747
23748    fn member_function_linkage(source: &str) -> CallableLinkage {
23749        let mut parser = tree_sitter::Parser::new();
23750        parser
23751            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23752            .unwrap();
23753        let tree = parser.parse(source, None).unwrap();
23754        let ancestry = ParentIndex::new(tree.root_node());
23755        let mut stack = vec![tree.root_node()];
23756        while let Some(node) = stack.pop() {
23757            if node.kind() == "function_definition" {
23758                let mut current = node.parent();
23759                while let Some(parent) = current {
23760                    if matches!(
23761                        parent.kind(),
23762                        "class_specifier" | "struct_specifier" | "union_specifier"
23763                    ) {
23764                        return cpp_callable_linkage(node, source, &ancestry);
23765                    }
23766                    current = parent.parent();
23767                }
23768            }
23769            let mut cursor = node.walk();
23770            stack.extend(node.named_children(&mut cursor));
23771        }
23772        panic!("fixture has no member function definition");
23773    }
23774
23775    #[test]
23776    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
23777        assert_eq!(
23778            member_function_linkage("struct Named { int method() { return 1; } };"),
23779            CallableLinkage::External
23780        );
23781        assert_eq!(
23782            member_function_linkage(
23783                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
23784            ),
23785            CallableLinkage::Internal
23786        );
23787        assert_eq!(
23788            member_function_linkage("struct { int method() { return 1; } } instance;"),
23789            CallableLinkage::Internal
23790        );
23791        assert_eq!(
23792            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
23793            CallableLinkage::Internal
23794        );
23795    }
23796
23797    #[test]
23798    #[hardwon(
23799        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23800    )]
23801    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
23802        let source = r#"
23803#ifndef PROTON_VALUE_HPP
23804#define PROTON_VALUE_HPP
23805namespace proton {
23806namespace internal {
23807class value_base {
23808  protected:
23809    internal::data& data();
23810    internal::data data_;
23811  friend class codec::encoder;
23812  friend class codec::decoder;
23813};
23814}
23815class value : public internal::value_base, private internal::comparable<value> {
23816  private:
23817    template<class T, class U=void> struct assignable :
23818        public std::enable_if<codec::is_encodable<T>::value, U> {};
23819    template<class U> struct assignable<value, U> {};
23820  public:
23821    PN_CPP_EXTERN value();
23822    PN_CPP_EXTERN value(const value&);
23823    PN_CPP_EXTERN value& operator=(const value&);
23824    PN_CPP_EXTERN value(value&&);
23825    PN_CPP_EXTERN value& operator=(value&&);
23826    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
23827    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
23828        codec::encoder e(*this);
23829        e << x;
23830        return *this;
23831    }
23832    PN_CPP_EXTERN type_id type() const;
23833    PN_CPP_EXTERN bool empty() const;
23834    PN_CPP_EXTERN void clear();
23835    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
23836    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
23837  friend PN_CPP_EXTERN void swap(value&, value&);
23838  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
23839  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
23840  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
23841    value(pn_data_t* d);
23842    void reset(pn_data_t* d = 0);
23843};
23844}
23845#endif
23846"#;
23847        let mut parser = tree_sitter::Parser::new();
23848        parser
23849            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23850            .unwrap();
23851        let tree = parser.parse(source, None).unwrap();
23852        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
23853        let parsed = parse_cpp_file(&file, source, &tree);
23854        // The macro-declaration grammar parses the class and its decorated
23855        // members cleanly, so the constructors mint with their true
23856        // class-owned identity (`proton.value.value`) rather than the
23857        // namespace-scope identity the fragment recovery produced before.
23858        let macro_constructors = parsed
23859            .signature_metadata
23860            .iter()
23861            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
23862            .flat_map(|(_, metadata)| metadata)
23863            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
23864            .collect::<Vec<_>>();
23865
23866        assert_eq!(
23867            macro_constructors.len(),
23868            3,
23869            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
23870            parsed.declarations()
23871        );
23872        assert!(
23873            macro_constructors.iter().all(|metadata| {
23874                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
23875            }),
23876            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
23877        );
23878    }
23879
23880    #[test]
23881    #[hardwon(
23882        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
23883    )]
23884    fn recovered_export_class_typedef_uses_displaced_alias_name() {
23885        let source = r#"
23886namespace spi {
23887class Filter {
23888public:
23889    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
23890};
23891}
23892namespace filter {
23893class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
23894{
23895public:
23896    typedef spi::Filter BASE_CLASS;
23897    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
23898    BEGIN_LOG4CXX_CAST_MAP()
23899    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
23900    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
23901    END_LOG4CXX_CAST_MAP()
23902    FilterDecision decide() const;
23903};
23904}
23905"#;
23906        let mut parser = tree_sitter::Parser::new();
23907        parser
23908            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23909            .unwrap();
23910        let tree = parser.parse(source, None).unwrap();
23911        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
23912        let parsed = parse_cpp_file(&file, source, &tree);
23913        assert!(
23914            parsed.declarations().iter().any(|unit| {
23915                unit.is_class()
23916                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
23917                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
23918            }),
23919            "the displaced typedef alias must retain its declared name: {:#?}",
23920            parsed.declarations()
23921        );
23922        assert!(
23923            parsed
23924                .declarations()
23925                .iter()
23926                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
23927            "the qualified underlying type must not become a false nested alias: {:#?}",
23928            parsed.declarations()
23929        );
23930    }
23931
23932    #[test]
23933    #[hardwon(
23934        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23935    )]
23936    fn exported_single_base_recovery_uses_displaced_class_name() {
23937        let source = r#"
23938class CORE_EXPORT QgsPoint : public AbstractGeometry
23939{
23940    Q_GADGET
23941
23942    Q_PROPERTY( double x READ x WRITE setX )
23943    Q_PROPERTY( double y READ y WRITE setY )
23944    Q_PROPERTY( double z READ z WRITE setZ )
23945    Q_PROPERTY( double m READ m WRITE setM )
23946
23947  public:
23948#ifndef SIP_RUN
23949    QgsPoint(
23950      double x = std::numeric_limits<double>::quiet_NaN(),
23951      double y = std::numeric_limits<double>::quiet_NaN(),
23952      double z = std::numeric_limits<double>::quiet_NaN(),
23953      double m = std::numeric_limits<double>::quiet_NaN(),
23954      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
23955    );
23956#else
23957    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 )];
23958    % MethodCode
23959    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
23960    {
23961      int state;
23962      sipIsErr = 0;
23963      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
23964      if ( !sipIsErr )
23965      {
23966        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
23967      }
23968      sipReleaseType( p, sipType_QgsPointXY, state );
23969    }
23970    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
23971    {
23972      int state;
23973      sipIsErr = 0;
23974
23975      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
23976      if ( !sipIsErr )
23977      {
23978        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
23979      }
23980      sipReleaseType( p, sipType_QPointF, state );
23981    }
23982    else if (
23983      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
23984      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
23985      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
23986      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
23987    {
23988      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
23989      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
23990      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
23991      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
23992      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
23993      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
23994    }
23995    else // Invalid ctor arguments
23996    {
23997      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
23998      sipIsErr = 1;
23999    }
24000    % End
24001#endif
24002
24003    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
24004    explicit QgsPoint( QPointF p ) SIP_SKIP;
24005    explicit QgsPoint(
24006      Qgis::WkbType wkbType,
24007      double x = std::numeric_limits<double>::quiet_NaN(),
24008      double y = std::numeric_limits<double>::quiet_NaN(),
24009      double z = std::numeric_limits<double>::quiet_NaN(),
24010      double m = std::numeric_limits<double>::quiet_NaN()
24011    ) SIP_SKIP;
24012    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24013    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
24014    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
24015#ifndef SIP_RUN
24016  private:
24017    bool fuzzyHelper(
24018      double epsilon,
24019      const AbstractGeometry &other,
24020      bool is3DFlag,
24021      bool isMeasureFlag
24022    ) const
24023    {
24024      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
24025    }
24026#endif
24027};
24028class Ordinary : public Base { public: Ordinary(); };
24029class API_EXPORT Plain { public: Plain(); };
24030class API_EXPORT : public Base {};
24031class
24032PN_CPP_CLASS_EXTERN Sender : public Link {
24033    Sender();
24034    struct impl;
24035    struct impl& get_impl() const;
24036};
24037class thread_ctx_t {};
24038class ctx_t ZMQ_FINAL : public thread_ctx_t {
24039    bool start();
24040};
24041"#;
24042        let mut parser = tree_sitter::Parser::new();
24043        parser
24044            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24045            .unwrap();
24046        let tree = parser.parse(source, None).unwrap();
24047        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
24048        let parsed = parse_cpp_file(&file, source, &tree);
24049        let declarations = parsed.declarations();
24050
24051        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
24052            assert!(
24053                declarations
24054                    .iter()
24055                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
24056                "missing recovered class {expected}: {declarations:#?}"
24057            );
24058        }
24059        let qgs_point = declarations
24060            .iter()
24061            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
24062            .expect("recovered QgsPoint class");
24063        assert_eq!(
24064            parsed.raw_supertypes.get(qgs_point),
24065            Some(&vec!["AbstractGeometry".to_string()]),
24066            "single-base export recovery must retain its displaced base"
24067        );
24068        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
24069        assert!(
24070            parsed
24071                .navigation_ranges
24072                .get(qgs_point)
24073                .is_some_and(|ranges| {
24074                    !ranges.is_empty()
24075                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
24076                }),
24077            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
24078            parsed.navigation_ranges.get(qgs_point)
24079        );
24080        let sender = declarations
24081            .iter()
24082            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
24083            .expect("recovered Sender class");
24084        assert_eq!(
24085            parsed.raw_supertypes.get(sender),
24086            Some(&vec!["Link".to_string()]),
24087            "post-declarator export recovery must retain its displaced base"
24088        );
24089        let recovered_member = declarations
24090            .iter()
24091            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
24092            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
24093        assert_eq!(
24094            parsed
24095                .signature_metadata
24096                .get(recovered_member)
24097                .and_then(|metadata| metadata.first())
24098                .and_then(SignatureMetadata::callable_linkage),
24099            Some(CallableLinkage::External),
24100            "a named recovered class's members have external linkage"
24101        );
24102        let ctx = declarations
24103            .iter()
24104            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
24105            .expect("recovered ctx_t class");
24106        assert_eq!(
24107            parsed.raw_supertypes.get(ctx),
24108            Some(&vec!["thread_ctx_t".to_string()]),
24109            "postfix export-macro recovery must retain its displaced base"
24110        );
24111        assert!(
24112            declarations.iter().any(|unit| {
24113                unit.is_function()
24114                    && unit.fq_name() == "QgsPoint.QgsPoint"
24115                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
24116            }),
24117            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
24118        );
24119        assert!(
24120            declarations.iter().all(|unit| {
24121                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
24122            }),
24123            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
24124        );
24125    }
24126
24127    #[test]
24128    #[hardwon(
24129        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24130    )]
24131    fn function_like_export_macro_classes_keep_names_and_base_edges() {
24132        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
24133        // error: a plain body, a single base, `final` without a base, `final`
24134        // with one base, and `final` with a comma-separated base list.
24135        let source = r#"
24136namespace api {
24137class PROJECT_PUBLIC_API(2, 0) Prelude {
24138  public:
24139    Prelude();
24140};
24141class PROJECT_PUBLIC_API(2, 0) Base {
24142  public:
24143    Base(int value);
24144};
24145class PROJECT_PUBLIC_API(2, 0) Mixin {
24146  public:
24147    Mixin();
24148};
24149class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
24150  public:
24151    Adopted(int value);
24152};
24153class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
24154  public:
24155    Derived(int value);
24156};
24157class PROJECT_PUBLIC_API(2, 0) Solo final {
24158  public:
24159    Solo();
24160};
24161class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
24162  public:
24163    Blended(int value);
24164};
24165class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
24166  public:
24167    Woven(int value);
24168};
24169} // namespace api
24170"#;
24171        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
24172        let declarations = parsed.declarations();
24173        let class_named = |name: &str| {
24174            declarations
24175                .iter()
24176                .find(|unit| unit.is_class() && unit.fq_name() == name)
24177                .unwrap_or_else(|| {
24178                    panic!("missing function-like export macro class {name}: {declarations:#?}")
24179                })
24180        };
24181        let base = class_named("api.Base");
24182        class_named("api.Prelude");
24183        class_named("api.Mixin");
24184
24185        assert_eq!(
24186            parsed.raw_supertypes.get(class_named("api.Adopted")),
24187            Some(&vec!["Base".to_string()])
24188        );
24189        assert_eq!(
24190            parsed.raw_supertypes.get(class_named("api.Derived")),
24191            Some(&vec!["Base".to_string()])
24192        );
24193        assert_eq!(
24194            parsed.raw_supertypes.get(class_named("api.Solo")),
24195            None,
24196            "a final class without a base list must not invent a supertype"
24197        );
24198        assert_eq!(
24199            parsed.raw_supertypes.get(class_named("api.Blended")),
24200            Some(&vec!["Base".to_string(), "Mixin".to_string()])
24201        );
24202        assert_eq!(
24203            parsed.raw_supertypes.get(class_named("api.Woven")),
24204            Some(&vec!["Base".to_string(), "Mixin".to_string()])
24205        );
24206        assert!(
24207            declarations
24208                .iter()
24209                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24210            "the export macro must not become a declaration: {declarations:#?}"
24211        );
24212        assert!(
24213            declarations.iter().all(|unit| !matches!(
24214                unit.identifier(),
24215                "final" | "public" | "protected" | "private"
24216            )),
24217            "the head specifiers must not become declarations: {declarations:#?}"
24218        );
24219        assert!(
24220            parsed
24221                .navigation_ranges
24222                .get(base)
24223                .is_some_and(|ranges| !ranges.is_empty()),
24224            "the recovered base must retain a navigable declaration range"
24225        );
24226    }
24227
24228    #[test]
24229    #[hardwon(
24230        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
24231    )]
24232    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
24233        // #2557: the class name is the last identifier before the head ends
24234        // (`final`, the base clause `:`, or the body), whatever its spelling.
24235        // A class named in capitals (`X509_CA`) is a class, and an object-like
24236        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
24237        // the control whose spelling never mattered.
24238        let source = r#"
24239namespace api {
24240class PROJECT_PUBLIC_API(2, 0) Base {
24241  public:
24242    Base();
24243};
24244class PROJECT_PUBLIC_API(2, 0) Mixin {
24245  public:
24246    Mixin();
24247};
24248class PROJECT_PUBLIC_API(2, 0) Name {
24249  public:
24250    Name();
24251};
24252class PROJECT_PUBLIC_API(2, 0) X509_CA final {
24253  public:
24254    X509_CA();
24255};
24256class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
24257  public:
24258    HSS_LMS_KEY();
24259};
24260class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
24261  public:
24262    GOST_3410();
24263};
24264class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
24265  public:
24266    PKCS11_RSA();
24267};
24268class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
24269  public:
24270    Plain();
24271};
24272class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
24273  public:
24274    Decorated();
24275};
24276class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
24277  public:
24278    Layered();
24279};
24280} // namespace api
24281"#;
24282        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
24283        let declarations = parsed.declarations();
24284        let class_named = |name: &str| {
24285            declarations
24286                .iter()
24287                .find(|unit| unit.is_class() && unit.fq_name() == name)
24288                .unwrap_or_else(|| {
24289                    panic!("missing function-like export macro class {name}: {declarations:#?}")
24290                })
24291        };
24292        for (name, bases) in [
24293            ("api.Name", None),
24294            ("api.X509_CA", None),
24295            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
24296            ("api.GOST_3410", Some(vec!["Base"])),
24297            ("api.PKCS11_RSA", None),
24298            ("api.Plain", None),
24299            ("api.Decorated", Some(vec!["Base"])),
24300            ("api.Layered", Some(vec!["Base", "Mixin"])),
24301        ] {
24302            let expected =
24303                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
24304            assert_eq!(
24305                parsed.raw_supertypes.get(class_named(name)),
24306                expected.as_ref(),
24307                "{name}"
24308            );
24309        }
24310        assert!(
24311            declarations.iter().all(|unit| !matches!(
24312                unit.identifier(),
24313                "PROJECT_PUBLIC_API"
24314                    | "OTHER_MACRO"
24315                    | "FIRST_MACRO"
24316                    | "SECOND_MACRO"
24317                    | "final"
24318                    | "public"
24319            )),
24320            "macros and head specifiers must not become declarations: {declarations:#?}"
24321        );
24322    }
24323
24324    #[test]
24325    #[hardwon(
24326        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
24327    )]
24328    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
24329        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
24330        // head, the body's `{` and its first member in one declaration-scope
24331        // ERROR, scatters the remaining members across the container's
24332        // siblings, and ends the container on the class's own `}`. The class
24333        // was lost and the macro minted a class of its own.
24334        let source = r#"
24335namespace api {
24336
24337/**
24338* Doc comment
24339*/
24340class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
24341   public:
24342      /**
24343      * Construct from a point.
24344      */
24345      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
24346
24347#if defined(PROJECT_HAS_LEGACY_POINT)
24348      /**
24349      * Construct from a legacy point.
24350      */
24351      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
24352#endif
24353
24354      std::string algo_name() const override;
24355
24356      AlgorithmIdentifier algorithm_identifier() const override;
24357};
24358
24359}
24360"#;
24361        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
24362        let declarations = parsed.declarations();
24363        let class = declarations
24364            .iter()
24365            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
24366            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
24367        assert_eq!(
24368            parsed.raw_supertypes.get(class),
24369            Some(&vec!["BaseKey".to_string()]),
24370            "the virtual base is the class's base: {declarations:#?}"
24371        );
24372        for member in [
24373            "api.VirtualBased.algo_name",
24374            "api.VirtualBased.algorithm_identifier",
24375        ] {
24376            assert!(
24377                declarations
24378                    .iter()
24379                    .any(|unit| unit.is_function() && unit.fq_name() == member),
24380                "{member} must be owned by the recovered class: {declarations:#?}"
24381            );
24382        }
24383        assert!(
24384            declarations
24385                .iter()
24386                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24387            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
24388        );
24389    }
24390
24391    #[test]
24392    #[hardwon(
24393        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
24394    )]
24395    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
24396        // #2924: object-like macro lines before the head demote the `class`
24397        // keyword to a bare identifier and make the macro invocation an
24398        // `init_declarator`, so the head arrives as a `declaration` rather than
24399        // as an ERROR carrying a `class_specifier`. The name is still the last
24400        // identifier before `final`, and `virtual` is a base specifier rather
24401        // than a base.
24402        let source = r#"
24403namespace api {
24404
24405DIAGNOSTIC_PUSH
24406DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
24407
24408class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
24409                                               public virtual api::Inner::Key {
24410   public:
24411      std::string algo_name() const override;
24412};
24413
24414DIAGNOSTIC_POP
24415
24416}
24417"#;
24418        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
24419        let declarations = parsed.declarations();
24420        let class = declarations
24421            .iter()
24422            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
24423            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
24424        assert_eq!(
24425            parsed.raw_supertypes.get(class),
24426            Some(&vec![
24427                "api::Outer::Key".to_string(),
24428                "api::Inner::Key".to_string()
24429            ]),
24430            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
24431        );
24432        assert!(
24433            declarations
24434                .iter()
24435                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
24436            "the member is owned by the recovered class: {declarations:#?}"
24437        );
24438        assert!(
24439            declarations
24440                .iter()
24441                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24442            "the macro invocation must not mint a declaration: {declarations:#?}"
24443        );
24444    }
24445
24446    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
24447        format!(
24448            r#"
24449namespace api {{
24450class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
24451   public:
24452      /** Return a descriptive string. */
24453      const char* what() const noexcept override {{ return m_msg.c_str(); }}
24454
24455      /** Return the type of error. */
24456      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
24457
24458      /** Return an associated error code. */
24459      virtual int error_code() const noexcept {{ return 0; }}
24460
24461      /** Avoid throwing the base directly. */
24462      explicit Exception(std::string_view msg);
24463
24464      /** Avoid throwing the base directly. */
24465      Exception(const char* prefix, std::string_view msg);
24466
24467      /** Avoid throwing the base directly. */
24468      Exception(std::string_view msg, const std::exception& e);
24469
24470   private:
24471      std::string m_msg;
24472}};
24473
24474class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
24475   public:
24476      explicit {name}(std::string_view msg);
24477
24478      explicit {name}(std::string_view msg, std::string_view where);
24479
24480      {name}(std::string_view msg, const std::exception& e);
24481
24482      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
24483}};
24484}} // namespace api
24485"#
24486        )
24487    }
24488
24489    #[test]
24490    #[hardwon(
24491        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
24492    )]
24493    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
24494        // The class embedded in a preceding malformed body follows the same
24495        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
24496        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
24497            let source = function_like_export_class_fixture(head, name);
24498            let mut parser = tree_sitter::Parser::new();
24499            parser
24500                .set_language(&tree_sitter_cpp::LANGUAGE.into())
24501                .expect("set C++ grammar");
24502            let tree = parser.parse(&source, None).expect("parse fixture");
24503            let mut embedded = Vec::new();
24504            let mut stack = vec![tree.root_node()];
24505            while let Some(node) = stack.pop() {
24506                embedded.extend(
24507                    recover_embedded_function_like_export_classes(node, &source)
24508                        .into_iter()
24509                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
24510                );
24511                let mut cursor = node.walk();
24512                stack.extend(node.named_children(&mut cursor));
24513            }
24514            assert!(
24515                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
24516                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
24517                tree.root_node().to_sexp()
24518            );
24519            assert!(
24520                embedded
24521                    .iter()
24522                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
24523                "{head}: the object-like macro is not a class: {embedded:#?}"
24524            );
24525
24526            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
24527            let declarations = parsed.declarations();
24528            let class = declarations
24529                .iter()
24530                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
24531                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
24532            assert_eq!(
24533                parsed.raw_supertypes.get(class),
24534                Some(&vec!["Exception".to_string()]),
24535                "{head}"
24536            );
24537            assert!(
24538                declarations
24539                    .iter()
24540                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
24541                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
24542            );
24543        }
24544    }
24545
24546    #[test]
24547    #[hardwon(
24548        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
24549    )]
24550    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
24551        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
24552        // `class MACRO(3, 6) Name final :` in the error, the first base as the
24553        // `type` of the following declaration, and closes that declaration with
24554        // a zero-width `MISSING identifier`. Reading the head by position must
24555        // not take that missing node for the class name: an empty name panics
24556        // at `FqName` construction, which took the Botan corpus replay down
24557        // (#2557). The head's tokens straddle the two nodes, so the positional
24558        // read spans both and recovers the class rather than leaving the
24559        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
24560        let source = r#"
24561namespace api {
24562class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
24563                                                    public virtual Botan::EC_PublicKey {
24564   public:
24565      std::string algo_name() const override { return "ECDSA"; }
24566};
24567} // namespace api
24568"#;
24569        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
24570        let declarations = parsed.declarations();
24571        assert!(
24572            declarations
24573                .iter()
24574                .all(|unit| !unit.identifier().is_empty()),
24575            "no declaration may carry an empty name: {declarations:#?}"
24576        );
24577        assert!(
24578            declarations
24579                .iter()
24580                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
24581            "macros and head specifiers must not become declarations: {declarations:#?}"
24582        );
24583        let class = declarations
24584            .iter()
24585            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
24586            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
24587        assert_eq!(
24588            parsed.raw_supertypes.get(class),
24589            Some(&vec![
24590                "Botan::TPM2::PublicKey".to_string(),
24591                "Botan::EC_PublicKey".to_string()
24592            ]),
24593            "both qualified virtual bases are bases: {declarations:#?}"
24594        );
24595        let source_facts = parsed.source_facts.as_ref().unwrap();
24596        let declaration = parsed
24597            .source_declaration_units
24598            .iter()
24599            .find(|(_, unit)| unit == class)
24600            .map(|(declaration, _)| *declaration)
24601            .expect("recovered class source bridge");
24602        let bases = source_facts
24603            .cpp
24604            .as_ref()
24605            .unwrap()
24606            .declarations
24607            .iter()
24608            .find(|fact| fact.declaration == declaration)
24609            .expect("recovered class source fact")
24610            .bases
24611            .clone();
24612        assert_eq!(
24613            bases
24614                .iter()
24615                .map(|base| base.components.clone())
24616                .collect::<Vec<_>>(),
24617            vec![
24618                vec!["Botan", "TPM2", "PublicKey"],
24619                vec!["Botan", "EC_PublicKey"],
24620            ],
24621            "generalized recovery must publish structured base components"
24622        );
24623        assert!(
24624            bases.iter().all(|base| base.is_virtual),
24625            "generalized recovery must preserve virtual base qualifiers"
24626        );
24627        assert!(
24628            declarations
24629                .iter()
24630                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
24631            "the head must not mint a macro-named class: {declarations:#?}"
24632        );
24633        let mixed_source = source.replace(
24634            "public virtual Botan::EC_PublicKey",
24635            "public Botan::EC_PublicKey",
24636        );
24637        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
24638        let qualifiers = mixed
24639            .source_facts
24640            .as_ref()
24641            .unwrap()
24642            .cpp
24643            .as_ref()
24644            .unwrap()
24645            .declarations
24646            .iter()
24647            .flat_map(|declaration| &declaration.bases)
24648            .map(|base| base.is_virtual)
24649            .collect::<Vec<_>>();
24650        assert_eq!(
24651            qualifiers,
24652            [true, false],
24653            "base qualifiers must not cross the comma"
24654        );
24655    }
24656
24657    #[test]
24658    #[hardwon(
24659        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24660    )]
24661    fn function_like_export_class_survives_a_preceding_malformed_body() {
24662        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
24663        let mut parser = tree_sitter::Parser::new();
24664        parser
24665            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24666            .expect("set C++ grammar");
24667        let tree = parser.parse(&source, None).expect("parse fixture");
24668        let mut stack = vec![tree.root_node()];
24669        let mut saw_embedded_shape = false;
24670        while let Some(node) = stack.pop() {
24671            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, &source)
24672                .iter()
24673                .any(|recovered| recovered.name == "Invalid_Argument");
24674            let mut cursor = node.walk();
24675            stack.extend(node.named_children(&mut cursor));
24676        }
24677        assert!(
24678            saw_embedded_shape,
24679            "fixture must retain the embedded error geometry: {}",
24680            tree.root_node().to_sexp()
24681        );
24682
24683        let parsed = parse_cpp_file(
24684            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
24685            &source,
24686            &tree,
24687        );
24688        let declarations = parsed.declarations();
24689        let exception = declarations
24690            .iter()
24691            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
24692            .expect("qualified-base export class");
24693        let invalid = declarations
24694            .iter()
24695            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
24696            .expect("class embedded in the preceding malformed body");
24697
24698        assert_eq!(
24699            parsed.raw_supertypes.get(exception),
24700            Some(&vec!["std::exception".to_string()])
24701        );
24702        assert_eq!(
24703            parsed.raw_supertypes.get(invalid),
24704            Some(&vec!["Exception".to_string()])
24705        );
24706        assert!(
24707            parsed.materialization_records.iter().any(|record| matches!(
24708                record,
24709                MaterializationRecord::RecoveredDeclaration { unit, .. }
24710                    if unit == invalid
24711            )),
24712            "the embedded class must retain recovery provenance: {:#?}",
24713            parsed.materialization_records
24714        );
24715    }
24716
24717    #[test]
24718    #[hardwon(
24719        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24720    )]
24721    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
24722        let source = r#"
24723public:
24724   explicit Lookup_Error(std::string_view err) : Exception(err) {}
24725
24726   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
24727"#;
24728        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
24729            .expect("reparse merged constructor body");
24730        let (range, body) =
24731            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
24732                .unwrap_or_else(|| {
24733                    panic!(
24734                        "the merged constructor must retain its structured declarator/body: {}",
24735                        tree.root_node().to_sexp()
24736                    )
24737                });
24738        assert_eq!(
24739            source.get(range).expect("constructor range"),
24740            "Lookup_Error(std::string_view err) : Exception(err) {}"
24741        );
24742        assert_eq!(node_text(body, source), "{}");
24743    }
24744
24745    #[test]
24746    #[hardwon(
24747        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24748    )]
24749    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
24750        let positive_source =
24751            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
24752        let mut parser = tree_sitter::Parser::new();
24753        parser
24754            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24755            .unwrap();
24756        let positive_tree = parser.parse(positive_source, None).unwrap();
24757        assert!(cpp_reparsed_members_are_indexable(
24758            positive_tree.root_node(),
24759            positive_source
24760        ));
24761
24762        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
24763        let negative_tree = parser.parse(negative_source, None).unwrap();
24764        assert!(!cpp_reparsed_members_are_indexable(
24765            negative_tree.root_node(),
24766            negative_source
24767        ));
24768    }
24769
24770    #[test]
24771    #[hardwon(
24772        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24773    )]
24774    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
24775        let copy_control_source = r#"
24776public:
24777    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
24778    explicit Token(const Token* tok);
24779    ~Token();
24780    Token* astOperand1() { return nullptr; }
24781"#;
24782        let constraint_source = r#"
24783private:
24784    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
24785    static T *tokAtImpl(T *tok, int index) {
24786        return tok;
24787    }
24788
24789    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
24790    static T *linkAtImpl(T *tok, int index) {
24791        return tok;
24792    }
24793
24794public:
24795    int late() const { return 1; }
24796"#;
24797        let mut parser = tree_sitter::Parser::new();
24798        parser
24799            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24800            .unwrap();
24801        let copy_control_tree = parser
24802            .parse(copy_control_source, None)
24803            .expect("parse copy-control fixture");
24804        assert!(
24805            copy_control_tree.root_node().has_error(),
24806            "fixture must exercise adjacent copy-control recovery"
24807        );
24808        assert!(
24809            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
24810            "a complete late getter must remain recoverable after adjacent copy-control declarations"
24811        );
24812        let mut cursor = copy_control_tree.root_node().walk();
24813        assert!(
24814            copy_control_tree
24815                .root_node()
24816                .named_children(&mut cursor)
24817                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
24818            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
24819            copy_control_tree.root_node().to_sexp()
24820        );
24821        let constraint_tree = parser
24822            .parse(constraint_source, None)
24823            .expect("parse constraint-macro fixture");
24824        assert!(constraint_tree.root_node().has_error());
24825        assert!(
24826            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
24827            "complete constraint-macro members must not hide a later ordinary member"
24828        );
24829        let mut cursor = constraint_tree.root_node().walk();
24830        assert!(
24831            constraint_tree
24832                .root_node()
24833                .named_children(&mut cursor)
24834                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
24835                    child,
24836                    constraint_source
24837                )),
24838            "fixture must retain the split constraint-macro prefix/function geometry"
24839        );
24840    }
24841
24842    #[test]
24843    #[hardwon(
24844        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24845    )]
24846    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
24847        let source = r#"
24848struct Analyzer {
24849    struct Action {
24850        Action() = default;
24851        Action(const Action&) = default;
24852        Action& operator=(const Action& rhs) & = default;
24853
24854        template<class T,
24855                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
24856                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
24857        // NOLINTNEXTLINE(google-explicit-constructor)
24858        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
24859        {}
24860
24861        enum : std::uint16_t { None = 0, Read = (1 << 0) };
24862        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
24863
24864    private:
24865        unsigned int mFlag{};
24866    };
24867
24868    enum class Direction : unsigned char { Forward, Reverse };
24869    virtual Action analyze(Direction d) const = 0;
24870};
24871"#;
24872        let mut parser = tree_sitter::Parser::new();
24873        parser
24874            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24875            .unwrap();
24876        let tree = parser.parse(source, None).unwrap();
24877        assert!(tree.root_node().has_error());
24878        let root = tree.root_node();
24879        let outer = root
24880            .named_children(&mut root.walk())
24881            .find(|child| child.kind() == "ERROR")
24882            .expect("fragmented Analyzer prefix");
24883        let outer_recovered =
24884            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
24885        assert_eq!(outer_recovered.name, "Analyzer");
24886        let outer_tree = cpp_reparse_fragmented_class_body(
24887            source,
24888            outer_recovered.body.reparse_start,
24889            outer_recovered.body.reparse_end,
24890        )
24891        .expect("reparse Analyzer body");
24892        let outer_root = outer_tree.root_node();
24893        let action_prefix = outer_root
24894            .named_children(&mut outer_root.walk())
24895            .find(|child| child.kind() == "ERROR")
24896            .expect("fragmented Action prefix");
24897        let action_recovered = fragmented_class_body(action_prefix, source)
24898            .expect("structured Action fragment boundary");
24899        assert_eq!(action_recovered.name, "Action");
24900        let action_tree = cpp_reparse_fragmented_class_body(
24901            source,
24902            action_recovered.body.reparse_start,
24903            action_recovered.body.reparse_end,
24904        )
24905        .expect("reparse Action body");
24906        let action_root = action_tree.root_node();
24907        let macro_prefix = action_root
24908            .named_children(&mut action_root.walk())
24909            .find(|child| child.kind() == "ERROR")
24910            .expect("constraint macro prefix");
24911        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
24912            .expect("structured template macro prefix");
24913        let macro_companion =
24914            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
24915        assert!(
24916            cpp_reparsed_template_macro_constructor_companion_is_indexable(
24917                macro_companion,
24918                macro_parameter,
24919                source,
24920            ),
24921            "split constrained constructor must be admitted: {}",
24922            macro_companion.to_sexp()
24923        );
24924        assert!(
24925            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
24926            "complete Action body must pass the recovery gate: {}",
24927            action_tree.root_node().to_sexp()
24928        );
24929        assert!(
24930            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
24931            "complete Analyzer body must pass the recovery gate: {}",
24932            outer_tree.root_node().to_sexp()
24933        );
24934        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
24935        let parsed = parse_cpp_file(&file, source, &tree);
24936        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
24937            assert!(
24938                parsed
24939                    .declarations()
24940                    .iter()
24941                    .any(|unit| unit.fq_name() == expected),
24942                "missing recovered declaration {expected}: {:#?}",
24943                parsed.declarations()
24944            );
24945        }
24946        assert!(
24947            parsed
24948                .declarations()
24949                .iter()
24950                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
24951            "nested members must not remain flattened: {:#?}",
24952            parsed.declarations()
24953        );
24954    }
24955
24956    #[test]
24957    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
24958    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
24959        let source = "value() = default;\nint value();\n";
24960        let tree = crate::diagnostics::parse_cpp_tree(source);
24961        let root = tree.root_node();
24962        assert!(
24963            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
24964            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
24965            root.to_sexp()
24966        );
24967        assert!(
24968            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
24969            "a return-typed same-name declaration is not a constructor witness: {}",
24970            root.to_sexp()
24971        );
24972
24973        let damaged_source = r#"
24974value(int x) {
24975  broken(
24976}
24977value() = default;
24978int value();
24979"#;
24980        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
24981        let damaged_root = damaged_tree.root_node();
24982        // The macro-declaration grammar absorbs the whole damaged witness
24983        // into one errorful `function_definition`, so the default-assignment
24984        // expression lives inside the bogus body rather than as a top-level
24985        // sibling. The gate's answer must not change: the assignment is not
24986        // the class-named call, and the errorful same-name function is not a
24987        // separate constructor witness.
24988        let mut damaged_stack = vec![damaged_root];
24989        let mut damaged_assignment = None;
24990        while let Some(node) = damaged_stack.pop() {
24991            if node.kind() == "expression_statement" {
24992                damaged_assignment = Some(node);
24993                break;
24994            }
24995            let mut cursor = node.walk();
24996            damaged_stack.extend(node.named_children(&mut cursor));
24997        }
24998        let damaged_assignment =
24999            damaged_assignment.expect("damaged witness probe retains the assignment expression");
25000        let mut damaged_cursor = damaged_root.walk();
25001        let damaged_members = damaged_root
25002            .named_children(&mut damaged_cursor)
25003            .collect::<Vec<_>>();
25004        assert!(
25005            !cpp_reparsed_defaulted_constructor_is_indexable(
25006                damaged_assignment,
25007                &damaged_members,
25008                Some("value"),
25009                damaged_source,
25010            ),
25011            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
25012            damaged_root.to_sexp()
25013        );
25014        assert!(
25015            !cpp_reparsed_members_are_indexable_for_class(
25016                damaged_root,
25017                damaged_source,
25018                Some("value")
25019            ),
25020            "the damaged witness must not pass the member gate either: {}",
25021            damaged_root.to_sexp()
25022        );
25023    }
25024
25025    #[test]
25026    #[hardwon(
25027        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25028    )]
25029    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
25030        let source = r#"
25031raw_hash_set& operator=(raw_hash_set&& that) {
25032  return move_assign(
25033      std::move(that),
25034      typename AllocTraits::propagate_on_container_move_assignment());
25035}
25036
25037iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
25038  return {};
25039}
25040
25041void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
25042
25043iterator insert(const_iterator hint, value_type&& value)
25044    ABSL_ATTRIBUTE_LIFETIME_BOUND {
25045  return {};
25046}
25047
25048friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
25049  return left.size() == right.size();
25050}
25051
25052static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
25053  return static_cast<slot_type*>(buffer);
25054}
25055
25056protected:
25057// Included-range recovery can attach this comment to the template prefix.
25058template <class K>
25059void AssertOnFind([[maybe_unused]] const K& key) {
25060  Check(key);
25061}
25062"#;
25063        let mut parser = tree_sitter::Parser::new();
25064        parser
25065            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25066            .unwrap();
25067        let tree = parser.parse(source, None).unwrap();
25068        assert!(
25069            tree.root_node().has_error(),
25070            "the fixture must exercise tree-sitter's errorful member shapes"
25071        );
25072        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
25073
25074        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
25075        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
25076        assert!(!cpp_reparsed_members_are_indexable(
25077            incomplete_tree.root_node(),
25078            incomplete_source
25079        ));
25080
25081        let outside_error_source = "int foo() stray_attribute {}\n";
25082        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
25083        assert!(outside_error_tree.root_node().has_error());
25084        assert!(!cpp_reparsed_members_are_indexable(
25085            outside_error_tree.root_node(),
25086            outside_error_source
25087        ));
25088
25089        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
25090        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
25091        assert!(!cpp_reparsed_members_are_indexable(
25092            variable_initializer_tree.root_node(),
25093            variable_initializer_source
25094        ));
25095    }
25096
25097    #[test]
25098    #[hardwon(
25099        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25100    )]
25101    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
25102        let positive_source = r#"
25103std::pair<iterator, bool> insert(init_type&& value)
25104    ABSL_ATTRIBUTE_LIFETIME_BOUND
25105#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
25106  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
25107#endif
25108{
25109  return emplace(std::move(value));
25110}
25111"#;
25112        let mut parser = tree_sitter::Parser::new();
25113        parser
25114            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25115            .unwrap();
25116        let positive_tree = parser.parse(positive_source, None).unwrap();
25117        assert!(
25118            positive_tree.root_node().has_error(),
25119            "the fixture must exercise the split attribute/requires shape"
25120        );
25121        assert!(cpp_reparsed_members_are_indexable(
25122            positive_tree.root_node(),
25123            positive_source
25124        ));
25125
25126        let template_return_source = r#"
25127pair<int> insert(init_type&& value)
25128    ABSL_ATTRIBUTE_LIFETIME_BOUND
25129#if LANGUAGE_LEVEL >= 202002L
25130  requires(!Predicate<init_type>::value)
25131#endif
25132// Attributes and the function body may be separated by comments.
25133{
25134  return {};
25135}
25136"#;
25137        let template_return_tree = parser.parse(template_return_source, None).unwrap();
25138        assert!(
25139            cpp_reparsed_members_are_indexable(
25140                template_return_tree.root_node(),
25141                template_return_source
25142            ),
25143            "template-return attribute/requires tree: {}",
25144            template_return_tree.root_node().to_sexp()
25145        );
25146
25147        let no_body_source = r#"
25148std::pair<iterator, bool> insert(init_type&& value)
25149    ABSL_ATTRIBUTE_LIFETIME_BOUND
25150#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
25151  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
25152#endif
25153+ 0;
25154"#;
25155        let no_body_tree = parser.parse(no_body_source, None).unwrap();
25156        assert!(!cpp_reparsed_members_are_indexable(
25157            no_body_tree.root_node(),
25158            no_body_source
25159        ));
25160
25161        let extra_payload_source = r#"
25162pair<int> insert(init_type&& value)
25163    ABSL_ATTRIBUTE_LIFETIME_BOUND
25164#if LANGUAGE_LEVEL >= 202002L
25165  int unrelated;
25166  requires(Predicate<init_type>::value)
25167#endif
25168{
25169  return {};
25170}
25171"#;
25172        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
25173        assert!(!cpp_reparsed_members_are_indexable(
25174            extra_payload_tree.root_node(),
25175            extra_payload_source
25176        ));
25177
25178        let variable_initializer_source = r#"
25179int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
25180#if LANGUAGE_LEVEL >= 202002L
25181  requires(true)
25182#endif
25183{
25184  bad;
25185}
25186"#;
25187        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
25188        assert!(!cpp_reparsed_members_are_indexable(
25189            variable_initializer_tree.root_node(),
25190            variable_initializer_source
25191        ));
25192    }
25193
25194    #[test]
25195    #[hardwon(
25196        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25197    )]
25198    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
25199        let source = r#"namespace absl {
25200ABSL_NAMESPACE_BEGIN namespace container_internal {
25201
25202class raw_hash_set : public Base {
25203 public:
25204  using value_type = int;
25205
25206  template <class U,
25207            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
25208  void insert(U value) { (void)value; }
25209
25210  struct InsertSlot {
25211    raw_hash_set& s;
25212  };
25213};
25214
25215}
25216ABSL_NAMESPACE_END
25217}"#;
25218        let mut parser = tree_sitter::Parser::new();
25219        parser
25220            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25221            .unwrap();
25222        let tree = parser.parse(source, None).unwrap();
25223        let root = tree.root_node();
25224        let outer_namespace = root
25225            .named_children(&mut root.walk())
25226            .find(|child| child.kind() == "namespace_definition")
25227            .expect("outer absl namespace");
25228        let declaration_list = outer_namespace
25229            .child_by_field_name("body")
25230            .expect("outer namespace body");
25231        let sentinel_function = declaration_list
25232            .named_children(&mut declaration_list.walk())
25233            .find(|child| child.kind() == "function_definition")
25234            .expect("malformed namespace sentinel function");
25235        let ancestry = ParentIndex::new(root);
25236        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
25237            .expect("structured nested namespace sentinel");
25238        let fragmented =
25239            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
25240                .expect("fragmented raw_hash_set class");
25241        assert_eq!(fragmented.class_node.kind(), "ERROR");
25242        assert_eq!(fragmented.name, "raw_hash_set");
25243        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
25244
25245        let outer_scope =
25246            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
25247        let mut outer_siblings = Vec::new();
25248        push_cpp_sentinel_sibling_classes(
25249            &mut outer_siblings,
25250            declaration_list,
25251            sentinel.function,
25252            &outer_scope,
25253            source,
25254            &ancestry,
25255        );
25256        let [outer_shadow] = outer_siblings.as_slice() else {
25257            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
25258        };
25259        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
25260        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
25261
25262        let field = "    raw_hash_set& s;";
25263        let start = source.find(field).expect("InsertSlot field") + 4;
25264        let node = root
25265            .descendant_for_byte_range(start, start + "raw_hash_set".len())
25266            .expect("raw_hash_set type node");
25267        let recovered = cpp_sentinel_recovered_classes(root, source);
25268        let [deep_class] = recovered.as_slice() else {
25269            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
25270        };
25271        assert_eq!(
25272            deep_class.namespace_scope_components,
25273            vec!["absl", "container_internal"]
25274        );
25275        assert_eq!(
25276            deep_class.scope_components,
25277            vec!["absl", "container_internal", "raw_hash_set"]
25278        );
25279        assert!(
25280            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
25281                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
25282        );
25283
25284        assert_eq!(
25285            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
25286            Some(vec![
25287                "absl".to_string(),
25288                "container_internal".to_string(),
25289                "raw_hash_set".to_string(),
25290                "InsertSlot".to_string(),
25291            ])
25292        );
25293
25294        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
25295        let parsed = parse_cpp_file(&file, source, &tree);
25296        let raw_hash_set = parsed
25297            .declarations()
25298            .iter()
25299            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
25300            .expect("recovered raw_hash_set class");
25301        assert_eq!(
25302            raw_hash_set.fq_name(),
25303            "absl::container_internal.raw_hash_set",
25304            "the recovered declaration must publish under the deeper sentinel namespace"
25305        );
25306        assert_eq!(
25307            parsed.raw_supertypes.get(raw_hash_set),
25308            Some(&vec!["Base".to_string()]),
25309            "the structured base clause on the fragmented ERROR prefix must survive publication"
25310        );
25311        assert!(
25312            parsed.materialization_records.iter().any(|record| matches!(
25313                record,
25314                MaterializationRecord::RecoveredDeclaration { recovery, unit }
25315                    if unit == raw_hash_set && *recovery == deep_class.class_range
25316            )),
25317            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
25318            parsed.materialization_records
25319        );
25320    }
25321
25322    /// Issue #2358: recording an aggregate definition must not walk the whole
25323    /// file.
25324    ///
25325    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
25326    /// class-like shape that has a body, so the removal step runs once per
25327    /// aggregate. It used to `retain` over `top_level_declarations` and over
25328    /// *every* child list in the file on each of those calls, comparing whole
25329    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
25330    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
25331    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
25332    /// tens of thousands of times over and the C forward differential never
25333    /// finished.
25334    ///
25335    /// A definition the file has not already declared removes nothing, so the
25336    /// honest cost is zero regardless of how many other aggregates surround it.
25337    /// Two sizes an order of magnitude apart pin that the count is not merely
25338    /// small but independent of the file.
25339    ///
25340    /// The declaration walk answers every ancestor question from a
25341    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
25342    /// the root for each one (#2361). Substituting the index is only safe
25343    /// because it answers the identical question, so pin that on the shapes
25344    /// this file's recovery paths care about: anonymous and named aggregates,
25345    /// nested namespaces, templates, macro-displaced declarations and the
25346    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
25347    /// too -- `Node::parent` walks the visible tree, not the named one.
25348    #[test]
25349    #[hardwon(
25350        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
25351    )]
25352    fn the_parent_index_answers_what_tree_sitter_answers() {
25353        const SHAPES: [&str; 5] = [
25354            "namespace outer { namespace inner { struct Tag { int field; }; } }",
25355            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
25356            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
25357            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
25358            "class API Broken : public First, public Second {\n  void member();\n",
25359        ];
25360        for source in SHAPES {
25361            let mut parser = tree_sitter::Parser::new();
25362            parser
25363                .set_language(&tree_sitter_cpp::LANGUAGE.into())
25364                .unwrap();
25365            let tree = parser.parse(source, None).unwrap();
25366            let root = tree.root_node();
25367            let ancestry = ParentIndex::new(root);
25368            let mut nodes = 0usize;
25369            let mut stack = vec![root];
25370            while let Some(node) = stack.pop() {
25371                nodes += 1;
25372                assert_eq!(
25373                    node.parent().map(|parent| parent.id()),
25374                    ancestry.parent(node).map(|parent| parent.id()),
25375                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
25376                );
25377                let mut cursor = node.walk();
25378                stack.extend(node.children(&mut cursor));
25379            }
25380            assert!(nodes > 1, "{source:?} produced no tree to compare");
25381        }
25382    }
25383
25384    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
25385    /// index for every ancestor edge. Asking tree-sitter directly makes each
25386    /// edge re-descend from the root, turning declaration extraction on a
25387    /// deeply nested generated header from quadratic output work into a cubic
25388    /// tree walk. Exact query counts pin the route without a machine-dependent
25389    /// wall-clock ceiling.
25390    #[test]
25391    #[hardwon(
25392        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
25393    )]
25394    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
25395        const DEPTH: usize = 64;
25396        let mut source = String::new();
25397        for level in 0..DEPTH {
25398            writeln!(source, "namespace n{level} {{").unwrap();
25399        }
25400        source.push_str("int deepest(int value);\n");
25401        for _ in 0..DEPTH {
25402            source.push_str("}\n");
25403        }
25404
25405        let mut parser = tree_sitter::Parser::new();
25406        parser
25407            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25408            .unwrap();
25409        let tree = parser.parse(&source, None).unwrap();
25410        let root = tree.root_node();
25411        let ancestry = ParentIndex::new(root);
25412        let mut function_declarator = None;
25413        walk_named_tree_preorder(root, true, |node| {
25414            if node.kind() == "function_declarator" {
25415                function_declarator = Some(node);
25416                WalkControl::Break
25417            } else {
25418                WalkControl::Continue
25419            }
25420        });
25421        let function_declarator = function_declarator.expect("deepest function declarator");
25422        let ancestor_count =
25423            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
25424
25425        ancestry.reset_parent_query_count_for_test();
25426        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
25427        assert_eq!(DEPTH, lexical_scope.len());
25428        assert_eq!(
25429            ancestor_count + 1,
25430            ancestry.parent_query_count_for_test(),
25431            "lexical-scope ancestry bypassed the parent index"
25432        );
25433
25434        ancestry.reset_parent_query_count_for_test();
25435        assert_eq!(
25436            Some(DispatchExtensibility::Closed),
25437            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
25438        );
25439        assert_eq!(
25440            ancestor_count,
25441            ancestry.parent_query_count_for_test(),
25442            "dispatch ancestry bypassed the parent index"
25443        );
25444
25445        ancestry.reset_parent_query_count_for_test();
25446        assert_eq!(
25447            CallableLinkage::External,
25448            cpp_callable_linkage(function_declarator, &source, &ancestry)
25449        );
25450        assert_eq!(
25451            ancestor_count + 1,
25452            ancestry.parent_query_count_for_test(),
25453            "linkage ancestry bypassed the parent index"
25454        );
25455
25456        ancestry.reset_parent_query_count_for_test();
25457        assert!(!cpp_callable_is_structural_constructor(
25458            function_declarator,
25459            &source,
25460            &ancestry
25461        ));
25462        assert_eq!(
25463            ancestor_count + 1,
25464            ancestry.parent_query_count_for_test(),
25465            "constructor ancestry bypassed the parent index"
25466        );
25467    }
25468
25469    /// #3507: the one row-level dispatch fact C++ can state is stated only
25470    /// where the language states it -- in the class body.
25471    ///
25472    /// A call's exactness is not readable from a definition row: an
25473    /// out-of-line definition repeats no `virtual`, no `final`, and no base
25474    /// clause, and C++ inherits virtual-ness from a base that declares the
25475    /// member. Each shape below therefore has to answer for itself, and only
25476    /// a class body with no base clause at all can close a member that writes
25477    /// nothing.
25478    #[test]
25479    #[hardwon(
25480        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
25481    )]
25482    fn callable_dispatch_extensibility_comes_from_the_class_body() {
25483        const SOURCE: &str = "\
25484class Plain { public: long send(long order); };
25485class Derived : public Base { public: long send(long order); };
25486class Sealed : public Base { public: long send(long order) final; };
25487class Hiding : public Base { public: long send(long order) { return order; } };
25488class Virtual { public: virtual long send(long order); };
25489namespace shop { long free_call(long order); }
25490long Plain::send(long order) { return order; }
25491";
25492        let mut parser = tree_sitter::Parser::new();
25493        parser
25494            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25495            .unwrap();
25496        let tree = parser.parse(SOURCE, None).unwrap();
25497        let root = tree.root_node();
25498        let ancestry = ParentIndex::new(root);
25499        let mut answers = Vec::new();
25500        walk_named_tree_preorder(root, true, |node| {
25501            if node.kind() != "function_declarator" {
25502                return WalkControl::Continue;
25503            }
25504            let name = cpp_function_declarator_name_node(node).expect("declarator name");
25505            answers.push((
25506                node_text(name, SOURCE).to_string(),
25507                cpp_callable_dispatch_extensibility(node, &ancestry),
25508            ));
25509            WalkControl::Continue
25510        });
25511        assert_eq!(
25512            answers,
25513            vec![
25514                ("send".to_string(), Some(DispatchExtensibility::Closed)),
25515                ("send".to_string(), Some(DispatchExtensibility::Open)),
25516                ("send".to_string(), Some(DispatchExtensibility::Closed)),
25517                ("send".to_string(), Some(DispatchExtensibility::Open)),
25518                ("send".to_string(), Some(DispatchExtensibility::Open)),
25519                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
25520                ("Plain::send".to_string(), None),
25521            ],
25522            "the class body states the fact; a qualified definition states none"
25523        );
25524    }
25525
25526    #[test]
25527    #[hardwon(
25528        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
25529    )]
25530    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
25531        const DISTINCT_PER_KIND: usize = 64;
25532        let mut source = String::new();
25533        for index in 0..DISTINCT_PER_KIND {
25534            writeln!(source, "typedef int Alias{index};").unwrap();
25535        }
25536        writeln!(source, "typedef long Alias0;").unwrap();
25537        for index in 0..DISTINCT_PER_KIND {
25538            writeln!(source, "#define MACRO_{index} {index}").unwrap();
25539        }
25540        writeln!(source, "#define MACRO_0 duplicate").unwrap();
25541        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
25542
25543        let mut parser = tree_sitter::Parser::new();
25544        parser
25545            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25546            .unwrap();
25547        let tree = parser.parse(&source, None).unwrap();
25548        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
25549
25550        start_declaration_identity_comparison_probe();
25551        let parsed = parse_cpp_file(&file, &source, &tree);
25552        let comparisons = finish_declaration_identity_comparison_probe();
25553
25554        assert_eq!(
25555            DISTINCT_PER_KIND + 1,
25556            parsed
25557                .declarations()
25558                .iter()
25559                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
25560                .count(),
25561            "every physical typedef alias declaration must be retained so \
25562             conditional branch guards stay available to the resolver"
25563        );
25564        assert_eq!(
25565            DISTINCT_PER_KIND + 1,
25566            parsed
25567                .declarations()
25568                .iter()
25569                .filter(|unit| {
25570                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
25571                })
25572                .count(),
25573            "distinct macro redefinitions must remain available to temporal lookup"
25574        );
25575        assert_eq!(
25576            2,
25577            parsed
25578                .declarations()
25579                .iter()
25580                .filter(|unit| {
25581                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
25582                })
25583                .count(),
25584            "function overloads must remain distinct"
25585        );
25586
25587        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
25588        assert!(
25589            comparisons <= dedup_inputs * 4,
25590            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
25591        );
25592    }
25593
25594    #[test]
25595    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
25596    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
25597        let negative_source = r#"namespace absl {
25598ABSL_NAMESPACE_BEGIN namespace container_internal {
25599template <typename T>
25600class broken {
25601 public:
25602  using value_type = T;
25603  T operator->() const { return &operator*(); }
25604}
25605}
25606"#;
25607        let mut parser = tree_sitter::Parser::new();
25608        parser
25609            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25610            .unwrap();
25611        let negative_tree = parser.parse(negative_source, None).unwrap();
25612        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
25613            .expect("the negative fixture must expose the malformed class node");
25614        assert!(
25615            broken.has_error(),
25616            "the negative fixture must retain a parser error"
25617        );
25618        // The macro-declaration grammar keeps the class body closed here; the
25619        // damage is the missing `;` and the unclosed namespace around it, so
25620        // the class's incompleteness is proven by the sentinel recognizers
25621        // finding no envelope for it rather than by a shared parent boundary.
25622        let mut envelope = None;
25623        {
25624            let mut stack = vec![negative_tree.root_node()];
25625            while let Some(node) = stack.pop() {
25626                if cpp_nested_namespace_sentinel(
25627                    node,
25628                    negative_source,
25629                    &ParentIndex::new(negative_tree.root_node()),
25630                )
25631                .is_some()
25632                    || cpp_root_namespace_sentinel(
25633                        node,
25634                        negative_source,
25635                        &ParentIndex::new(negative_tree.root_node()),
25636                    )
25637                    .is_some()
25638                    || cpp_sentinel_macro_class_region(node, negative_source).is_some()
25639                {
25640                    envelope = Some(node);
25641                    break;
25642                }
25643                stack.extend(node.named_children(&mut node.walk()));
25644            }
25645        }
25646        assert!(
25647            envelope.is_none(),
25648            "an incomplete class must not prove a sentinel envelope: {}",
25649            negative_tree.root_node().to_sexp()
25650        );
25651        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
25652        assert!(
25653            recovered
25654                .iter()
25655                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
25656            "an incomplete class must not borrow the namespace close: {recovered:#?}"
25657        );
25658
25659        let positive_source = r#"namespace absl {
25660ABSL_NAMESPACE_BEGIN namespace container_internal {
25661template <typename T>
25662class broken {
25663 public:
25664  using value_type = T;
25665  T operator->() const { return &operator*(); }
25666  using alias = value_type;
25667};
25668}
25669}
25670"#;
25671        let positive_tree = parser.parse(positive_source, None).unwrap();
25672        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
25673            .expect("the positive fixture must expose the broken class node");
25674        assert!(
25675            broken.has_error(),
25676            "the positive fixture must retain an internal parser error"
25677        );
25678        assert!(
25679            cpp_complete_class_body_close(broken).is_some(),
25680            "the positive fixture must expose a real class body close"
25681        );
25682        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
25683        assert!(
25684            recovered.iter().any(|class| {
25685                class.scope_components == ["absl", "container_internal", "broken"]
25686            }),
25687            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
25688        );
25689    }
25690
25691    #[test]
25692    #[hardwon(
25693        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
25694    )]
25695    fn sentinel_recovery_keeps_members_after_nested_body_close() {
25696        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
25697NLOHMANN_BASIC_JSON_TPL_DECLARATION
25698class basic_json {
25699 private:
25700  union storage {
25701    int value;
25702  } data;
25703 public:
25704  using late_alias = int;
25705  late_alias value() const;
25706};
25707NLOHMANN_JSON_NAMESPACE_END
25708"#;
25709        let mut parser = tree_sitter::Parser::new();
25710        parser
25711            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25712            .unwrap();
25713        let tree = parser.parse(source, None).unwrap();
25714        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
25715        let basic_json = recovered
25716            .iter()
25717            .find(|class| {
25718                class
25719                    .scope_components
25720                    .last()
25721                    .is_some_and(|name| name == "basic_json")
25722            })
25723            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
25724        let late_alias = source
25725            .find("late_alias value")
25726            .expect("late alias reference");
25727        assert!(
25728            basic_json.class_range.start_byte < late_alias
25729                && late_alias < basic_json.class_range.end_byte,
25730            "the recovered class range must include members after a nested close: {basic_json:#?}"
25731        );
25732    }
25733
25734    #[test]
25735    #[hardwon(
25736        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
25737    )]
25738    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
25739        let source = r#"namespace absl {
25740ABSL_NAMESPACE_BEGIN namespace container_internal {
25741template <typename T>
25742struct broken {
25743  using value_type = T;
25744};
25745}
25746
25747#ifdef OWNER_DEF
25748template <typename T>
25749typename broken<T>::value_type broken<T>::method() {
25750  value_type value{};
25751  return value;
25752}
25753#endif
25754
25755namespace sibling {
25756template <typename T>
25757typename broken<T>::value_type broken<T>::other() {
25758  value_type value{};
25759  return value;
25760}
25761}
25762
25763ABSL_NAMESPACE_END
25764}
25765"#;
25766        let mut parser = tree_sitter::Parser::new();
25767        parser
25768            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25769            .unwrap();
25770        let tree = parser.parse(source, None).unwrap();
25771        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
25772        let broken = recovered
25773            .iter()
25774            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
25775            .expect("the sentinel class must be recovered");
25776        let method_start = source
25777            .find("typename broken<T>::value_type broken<T>::method()")
25778            .expect("guarded sibling owner");
25779        let method_end = source[method_start..]
25780            .find("\n}")
25781            .map(|offset| method_start + offset + 2)
25782            .expect("guarded sibling owner close");
25783        assert!(
25784            broken
25785                .owner_ranges
25786                .iter()
25787                .any(|owner| owner.range.start_byte <= method_start
25788                    && method_end <= owner.range.end_byte),
25789            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
25790        );
25791        let sibling_start = source
25792            .find("typename broken<T>::value_type broken<T>::other()")
25793            .expect("nested namespace sibling owner");
25794        assert!(
25795            broken
25796                .owner_ranges
25797                .iter()
25798                .all(|owner| owner.range.start_byte > sibling_start
25799                    || owner.range.end_byte <= sibling_start),
25800            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
25801        );
25802    }
25803
25804    #[test]
25805    #[hardwon(
25806        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
25807    )]
25808    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
25809        let source = r#"#ifdef OUTER
25810namespace absl {
25811ABSL_NAMESPACE_BEGIN namespace container_internal {
25812template <typename T>
25813struct broken {
25814  using value_type = T;
25815};
25816}
25817}
25818
25819#ifdef OWNER_DEF
25820template <typename T>
25821typename broken<T>::value_type broken<T>::method() {
25822  value_type value{};
25823  return value;
25824}
25825#endif
25826#endif
25827"#;
25828        // The macro-declaration grammar keeps the sentinel envelope clean, so
25829        // the recovered-region index has nothing malformed to repair; the
25830        // declaration walk owns the recovery, and the out-of-line definition
25831        // after the missing ABSL_NAMESPACE_END must stay unowned.
25832        let parsed = parse_cpp_declarations(source, "discard-outer.h");
25833        assert!(
25834            parsed
25835                .declarations()
25836                .iter()
25837                .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
25838            "the sentinel class must be recovered: {:#?}",
25839            parsed.declarations()
25840        );
25841        let method = parsed
25842            .declarations()
25843            .iter()
25844            .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
25845            .expect("the guarded out-of-line definition");
25846        assert_eq!(
25847            method.fq_name(),
25848            "broken.method",
25849            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
25850            parsed.declarations()
25851        );
25852    }
25853
25854    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
25855    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
25856        let mut signatures = all_function_identities(parsed)
25857            .into_iter()
25858            .filter(|(name, _)| name == fq_name)
25859            .filter_map(|(_, signature)| signature)
25860            .collect::<Vec<_>>();
25861        signatures.sort();
25862        signatures.dedup();
25863        signatures
25864    }
25865
25866    #[test]
25867    #[hardwon(
25868        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
25869    )]
25870    fn callable_parameter_types_come_from_the_ast_parameter_list() {
25871        let source = r#"
25872template <typename T, ENABLE_BYTES(T)>
25873Vec256<T> DupOdd(Vec256<T> value) { return value; }
25874
25875struct Visitor {
25876  void fail(this auto const& self) {}
25877  void consume(this Visitor const& self, int count) {}
25878};
25879"#;
25880        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
25881        let dup_odd = parsed
25882            .declarations()
25883            .iter()
25884            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
25885            .expect("DupOdd declaration");
25886        assert_eq!(
25887            dup_odd.signature(),
25888            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
25889        );
25890        assert_eq!(
25891            parsed
25892                .signature_metadata
25893                .get(dup_odd)
25894                .and_then(|metadata| metadata.first())
25895                .and_then(SignatureMetadata::callable_parameter_types),
25896            Some(["Vec256<T>".to_string()].as_slice())
25897        );
25898
25899        let fail = parsed
25900            .declarations()
25901            .iter()
25902            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
25903            .expect("explicit-object member");
25904        assert_eq!(fail.signature(), Some("(this const auto &)"));
25905        let metadata = parsed
25906            .signature_metadata
25907            .get(fail)
25908            .and_then(|metadata| metadata.first())
25909            .expect("explicit-object signature metadata");
25910        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
25911        assert!(
25912            metadata
25913                .callable_arity()
25914                .is_some_and(|arity| arity.accepts(0))
25915        );
25916        let consume = parsed
25917            .declarations()
25918            .iter()
25919            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
25920            .expect("explicit-object member with an invocation argument");
25921        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
25922        let metadata = parsed
25923            .signature_metadata
25924            .get(consume)
25925            .and_then(|metadata| metadata.first())
25926            .expect("typed explicit-object signature metadata");
25927        assert_eq!(
25928            metadata.callable_parameter_types(),
25929            Some(["int".to_string()].as_slice())
25930        );
25931        assert!(
25932            metadata
25933                .callable_arity()
25934                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
25935        );
25936    }
25937
25938    #[test]
25939    #[hardwon(
25940        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
25941    )]
25942    fn trailing_qualifiers_survive_parameter_list_whitespace() {
25943        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
25944        // declarator's structure, so an out-of-line definition that spells its
25945        // parameter list with different whitespace than the declaration must
25946        // still carry it.
25947        let source = r#"
25948struct Widget {
25949  bool multiline(int settings, int supprs) const;
25950  bool doublespace(int settings, int supprs) const;
25951  bool noexcept_multiline(int settings, int supprs) noexcept;
25952  bool ref_multiline(int settings, int supprs) &&;
25953};
25954bool
25955Widget::multiline (int settings,
25956                   int supprs) const
25957{ return settings + supprs > 0; }
25958bool Widget::doublespace(int settings,  int supprs) const { return true; }
25959bool Widget::noexcept_multiline(int settings,
25960                                int supprs) noexcept { return true; }
25961bool Widget::ref_multiline(int settings,
25962                           int supprs) && { return true; }
25963"#;
25964        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
25965        assert_eq!(
25966            vec!["(int, int) const".to_string()],
25967            identity_signatures(&parsed, "Widget.multiline")
25968        );
25969        assert_eq!(
25970            vec!["(int, int) const".to_string()],
25971            identity_signatures(&parsed, "Widget.doublespace")
25972        );
25973        assert_eq!(
25974            vec!["(int, int) noexcept".to_string()],
25975            identity_signatures(&parsed, "Widget.noexcept_multiline")
25976        );
25977        assert_eq!(
25978            vec!["(int, int) &&".to_string()],
25979            identity_signatures(&parsed, "Widget.ref_multiline")
25980        );
25981    }
25982
25983    #[test]
25984    #[hardwon(
25985        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25986    )]
25987    fn macro_fragmented_plain_class_keeps_following_member_signature() {
25988        let source = r#"
25989struct CString {};
25990class CMessage {
25991public:
25992  CString GetParams(unsigned int index, unsigned int length = -1) const
25993      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
25994    return GetParamsColon(index, length);
25995  }
25996  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
25997};
25998CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
25999  return {};
26000}
26001"#;
26002        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
26003        assert_eq!(
26004            vec!["(unsigned int, unsigned int) const".to_string()],
26005            identity_signatures(&parsed, "CMessage.GetParamsColon")
26006        );
26007    }
26008
26009    #[test]
26010    #[hardwon(
26011        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
26012    )]
26013    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
26014        let source = r#"
26015#pragma once
26016#define DEMO_DEPRECATED(message)
26017namespace demo {
26018struct Base {
26019    static int aligned(int value) { return value; }
26020    int legacy(int value) const
26021        DEMO_DEPRECATED("use replacement()") { return value; }
26022    int replacement() const;
26023    void run(int value);
26024};
26025struct OtherBase {
26026    void run(int value);
26027    static int aligned(int value) { return value; }
26028};
26029struct Derived : Base {};
26030struct Override : Base {
26031    void run(int value);
26032    static int aligned(int value) { return value; }
26033};
26034struct RecoveredOverride : Base {
26035    int legacy(int value) const
26036        DEMO_DEPRECATED("use replacement()") { return value; }
26037    void run(int value);
26038};
26039struct Hidden : Base {
26040    void run(int first, int second);
26041    static int aligned(int first, int second) { return first + second; }
26042};
26043struct Ambiguous : Base, OtherBase {};
26044}
26045struct Global {};
26046"#;
26047        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
26048        let declarations = parsed.declarations();
26049        let fq_names = declarations
26050            .iter()
26051            .map(|unit| unit.fq_name())
26052            .collect::<std::collections::BTreeSet<_>>();
26053
26054        for expected in [
26055            "demo.Base",
26056            "demo.Base.aligned",
26057            "demo.Base.legacy",
26058            "demo.Base.replacement",
26059            "demo.Base.run",
26060            "demo.Derived",
26061            "demo.OtherBase",
26062            "demo.Override",
26063            "demo.RecoveredOverride",
26064            "demo.Hidden",
26065            "demo.Ambiguous",
26066            "Global",
26067        ] {
26068            assert!(
26069                fq_names.contains(expected),
26070                "missing {expected} from namespaced macro fragment: {declarations:#?}"
26071            );
26072        }
26073        assert!(
26074            !fq_names.contains("Derived"),
26075            "following class escaped its namespace: {declarations:#?}"
26076        );
26077        assert!(
26078            !fq_names.contains("demo.Global"),
26079            "global class crossed the recovered namespace boundary: {declarations:#?}"
26080        );
26081    }
26082
26083    #[test]
26084    #[hardwon(
26085        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
26086    )]
26087    fn trailing_qualifiers_still_separate_genuine_overloads() {
26088        // The qualifier must keep distinguishing the real C++ overload sets it
26089        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
26090        let source = r#"
26091struct Widget {
26092  int* slot(int index);
26093  const int* slot(int index) const;
26094  int log(int severity) &;
26095  int log(int severity) &&;
26096};
26097"#;
26098        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
26099        assert_eq!(
26100            vec!["(int)".to_string(), "(int) const".to_string()],
26101            identity_signatures(&parsed, "Widget.slot")
26102        );
26103        assert_eq!(
26104            vec!["(int) &".to_string(), "(int) &&".to_string()],
26105            identity_signatures(&parsed, "Widget.log")
26106        );
26107    }
26108
26109    #[test]
26110    #[hardwon(
26111        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
26112    )]
26113    fn virtual_specifier_is_not_part_of_the_identity_signature() {
26114        // `override` never appears on the out-of-line definition, and C++ does
26115        // not make it part of the signature, so it must not split the identity.
26116        let source = r#"
26117struct Base {
26118  virtual void run(int value) const;
26119};
26120struct Widget : Base {
26121  void run(int value) const override;
26122};
26123void Widget::run(int value) const {}
26124"#;
26125        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
26126        assert_eq!(
26127            vec!["(int) const".to_string()],
26128            identity_signatures(&parsed, "Widget.run")
26129        );
26130    }
26131
26132    #[test]
26133    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
26134    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
26135        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
26136        // the function type, so a declaration that spells `const int` and a
26137        // definition that spells `int` are one entity.
26138        let source = r#"
26139struct Widget {
26140  bool value_params(const int settings, const int supprs);
26141  void pointee_const(const int* p);
26142  void pointer_const(int* const p);
26143  void both_const(const int* const p);
26144  void reference_const(const int& p);
26145  void array_const(const int values[4]);
26146  void take(const int* p);
26147  void take(int* p);
26148};
26149bool Widget::value_params(int settings, int supprs) { return true; }
26150void Widget::pointer_const(int* p) {}
26151void Widget::both_const(const int* p) {}
26152"#;
26153        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
26154        assert_eq!(
26155            vec!["(int, int)".to_string()],
26156            identity_signatures(&parsed, "Widget.value_params")
26157        );
26158        assert_eq!(
26159            vec!["(int *)".to_string()],
26160            identity_signatures(&parsed, "Widget.pointer_const")
26161        );
26162        assert_eq!(
26163            vec!["(const int *)".to_string()],
26164            identity_signatures(&parsed, "Widget.both_const")
26165        );
26166        // The const that is not top-level still distinguishes the type.
26167        assert_eq!(
26168            vec!["(const int *)".to_string()],
26169            identity_signatures(&parsed, "Widget.pointee_const")
26170        );
26171        assert_eq!(
26172            vec!["(const int &)".to_string()],
26173            identity_signatures(&parsed, "Widget.reference_const")
26174        );
26175        assert_eq!(
26176            vec!["(const int [4])".to_string()],
26177            identity_signatures(&parsed, "Widget.array_const")
26178        );
26179        assert_eq!(
26180            vec!["(const int *)".to_string(), "(int *)".to_string()],
26181            identity_signatures(&parsed, "Widget.take")
26182        );
26183    }
26184
26185    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
26186        let mut parser = tree_sitter::Parser::new();
26187        parser
26188            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26189            .unwrap();
26190        let tree = parser.parse(source, None).unwrap();
26191        let start = source.find(callable_name).expect("callable declaration");
26192        let declarator =
26193            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
26194        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
26195    }
26196
26197    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
26198        let mut shapes = comparable_shapes(source, callable_name);
26199        assert_eq!(1, shapes.len(), "{shapes:?}");
26200        match shapes.remove(0) {
26201            CppComparableSlot::Shape(shape) => shape,
26202            other => panic!("expected a comparable shape, got {other:?}"),
26203        }
26204    }
26205
26206    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
26207        let mut current = shape.root();
26208        loop {
26209            match shape.node(current) {
26210                CppComparableNode::Named { .. } => return shape.node(current),
26211                CppComparableNode::Pointer { inner, .. }
26212                | CppComparableNode::Reference { inner }
26213                | CppComparableNode::Array { inner } => current = *inner,
26214                CppComparableNode::Generic { base, .. } => current = *base,
26215            }
26216        }
26217    }
26218
26219    #[test]
26220    #[hardwon(
26221        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
26222    )]
26223    fn comparable_shape_keeps_pointee_const() {
26224        assert_ne!(
26225            sole_comparable_shape("void f(const char* p);", "f("),
26226            sole_comparable_shape("void f(char* p);", "f(")
26227        );
26228    }
26229
26230    #[test]
26231    #[hardwon(
26232        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
26233    )]
26234    fn comparable_shape_keeps_inner_pointer_const() {
26235        assert_ne!(
26236            sole_comparable_shape("void f(int** p);", "f("),
26237            sole_comparable_shape("void f(int* const* p);", "f(")
26238        );
26239    }
26240
26241    #[test]
26242    #[hardwon(
26243        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
26244    )]
26245    fn comparable_shape_drops_top_level_pointer_const() {
26246        assert_eq!(
26247            sole_comparable_shape("void f(int* const p);", "f("),
26248            sole_comparable_shape("void f(int* p);", "f(")
26249        );
26250    }
26251
26252    #[test]
26253    #[hardwon(
26254        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
26255    )]
26256    fn comparable_shape_drops_top_level_base_const() {
26257        assert_eq!(
26258            sole_comparable_shape("void f(const int p);", "f("),
26259            sole_comparable_shape("void f(int p);", "f(")
26260        );
26261    }
26262
26263    #[test]
26264    #[hardwon(
26265        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
26266    )]
26267    fn comparable_shape_decays_top_level_array_to_pointer() {
26268        assert_eq!(
26269            sole_comparable_shape("void f(int a[3]);", "f("),
26270            sole_comparable_shape("void f(int* a);", "f(")
26271        );
26272        assert_eq!(
26273            sole_comparable_shape("void f(int* a[3]);", "f("),
26274            sole_comparable_shape("void f(int** a);", "f(")
26275        );
26276    }
26277
26278    #[test]
26279    #[hardwon(
26280        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
26281    )]
26282    fn comparable_shape_keeps_array_behind_pointer() {
26283        assert_ne!(
26284            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
26285            sole_comparable_shape("struct S { void f(int** a); };", "f(")
26286        );
26287    }
26288
26289    #[test]
26290    #[hardwon(
26291        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
26292    )]
26293    fn comparable_shape_records_written_name_and_lexical_scope() {
26294        let declared =
26295            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
26296        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
26297        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
26298            panic!("named leaf");
26299        };
26300        assert_eq!(["Msg".to_string()].as_slice(), name.path());
26301        assert_eq!(
26302            ["ns".to_string(), "S".to_string()].as_slice(),
26303            name.lexical_scope()
26304        );
26305        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
26306            panic!("named leaf");
26307        };
26308        assert_eq!(
26309            ["ns".to_string(), "Msg".to_string()].as_slice(),
26310            name.path()
26311        );
26312        assert!(name.lexical_scope().is_empty());
26313        assert_ne!(declared, defined);
26314    }
26315
26316    #[test]
26317    #[hardwon(
26318        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
26319    )]
26320    fn comparable_shape_marks_sized_primitive_leaf() {
26321        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
26322        let CppComparableNode::Named {
26323            name, primitive, ..
26324        } = comparable_named_leaf(&shape)
26325        else {
26326            panic!("named leaf");
26327        };
26328        assert!(primitive);
26329        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
26330        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
26331    }
26332
26333    #[test]
26334    #[hardwon(
26335        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
26336    )]
26337    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
26338        assert_eq!(
26339            vec![CppComparableSlot::Unstructured],
26340            comparable_shapes("void f(void (*cb)(int));", "f(")
26341        );
26342    }
26343
26344    #[test]
26345    #[hardwon(
26346        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
26347    )]
26348    fn comparable_shape_reports_ellipsis_slot() {
26349        let shapes = comparable_shapes("void f(int a, ...);", "f(");
26350        assert_eq!(2, shapes.len(), "{shapes:?}");
26351        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
26352    }
26353
26354    #[test]
26355    #[hardwon(
26356        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
26357    )]
26358    fn comparable_shape_keeps_template_argument_const() {
26359        assert_ne!(
26360            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
26361            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
26362        );
26363    }
26364
26365    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
26366    /// file-scope tag that a later `struct inner *` at file scope may name.
26367    #[test]
26368    #[hardwon(
26369        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
26370    )]
26371    fn c_file_mints_aggregate_member_tag_at_file_scope() {
26372        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
26373        let parsed = parse_cpp_declarations(source, "x.c");
26374        let declarations = parsed.declarations();
26375
26376        assert!(
26377            declarations
26378                .iter()
26379                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
26380            "expected a file-scope inner tag, got {declarations:?}"
26381        );
26382        assert!(
26383            declarations
26384                .iter()
26385                .all(|unit| unit.fq_name() != "outer$inner"),
26386            "expected no nested identity, got {declarations:?}"
26387        );
26388        assert!(
26389            declarations
26390                .iter()
26391                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
26392        );
26393        // Members still belong to their own aggregate.
26394        assert!(
26395            declarations
26396                .iter()
26397                .any(|unit| unit.fq_name() == "inner.value")
26398        );
26399        assert!(
26400            declarations
26401                .iter()
26402                .any(|unit| unit.fq_name() == "outer.item")
26403        );
26404
26405        let outer = declarations
26406            .iter()
26407            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
26408            .expect("outer");
26409        assert!(
26410            parsed
26411                .children
26412                .get(outer)
26413                .into_iter()
26414                .flatten()
26415                .all(|child| child.fq_name() != "inner"),
26416            "the tag must not hang off the aggregate it is written inside: {:?}",
26417            parsed.children
26418        );
26419    }
26420
26421    /// A header carries no compilation language of its own, and a `.cpp`
26422    /// translation unit really does declare a nested class. Both keep exactly
26423    /// the C++ extraction they had before the C dialect existed.
26424    #[test]
26425    #[hardwon(
26426        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
26427    )]
26428    fn header_and_cpp_files_keep_nested_tag_identity() {
26429        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
26430        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
26431            let parsed = parse_cpp_declarations(source, name);
26432            let declarations = parsed.declarations();
26433            assert!(
26434                declarations
26435                    .iter()
26436                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
26437                "{name} must keep the nested identity, got {declarations:?}"
26438            );
26439            assert!(
26440                declarations.iter().all(|unit| unit.fq_name() != "inner"),
26441                "{name} must not mint a file-scope tag, got {declarations:?}"
26442            );
26443            assert!(
26444                declarations
26445                    .iter()
26446                    .any(|unit| unit.fq_name() == "outer$inner.value")
26447            );
26448        }
26449    }
26450
26451    #[test]
26452    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
26453        let source = r#"
26454template <class K, class V, class Traits = CacheTraits<K>>
26455class ARCCache {
26456 public:
26457    struct ArcSizeInfo {
26458        void BSize() {}
26459    };
26460 private:
26461    struct BMapVal;
26462    struct BListVal;
26463    typedef std::unordered_map<K, BMapVal> BMap;
26464    typedef typename BMap::iterator bmap_iter;
26465    typedef typename std::list<BListVal> BList;
26466    typedef typename BList::iterator blist_iter;
26467    struct BListVal {
26468        bmap_iter map_iter;
26469        BListVal() = default;
26470    };
26471    struct DefinedVal {
26472        DefinedVal() {}
26473    };
26474};
26475"#;
26476        let tree = crate::diagnostics::parse_cpp_tree(source);
26477        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
26478        let cpp = crate::adapter::parse_cpp_file_in_dialect(
26479            &file,
26480            source,
26481            &tree,
26482            LanguageDialect::Standard(Language::Cpp),
26483        );
26484        let c =
26485            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
26486        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
26487            for fq_name in [
26488                "ARCCache$ArcSizeInfo.BSize",
26489                "ARCCache$BListVal.BListVal",
26490                "ARCCache$DefinedVal.DefinedVal",
26491            ] {
26492                assert!(
26493                    parsed
26494                        .declarations()
26495                        .iter()
26496                        .any(|unit| unit.fq_name() == fq_name),
26497                    "{dialect} reading must contain {fq_name}: {:#?}",
26498                    parsed.declarations()
26499                );
26500            }
26501            for flattened in [
26502                "ArcSizeInfo.BSize",
26503                "BListVal.BListVal",
26504                "DefinedVal.DefinedVal",
26505            ] {
26506                assert!(
26507                    parsed
26508                        .declarations()
26509                        .iter()
26510                        .all(|unit| unit.fq_name() != flattened),
26511                    "{dialect} reading must not flatten {flattened}: {:#?}",
26512                    parsed.declarations()
26513                );
26514            }
26515        }
26516    }
26517
26518    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
26519    #[test]
26520    #[hardwon(
26521        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
26522    )]
26523    fn uppercase_c_extension_keeps_cpp_tag_scope() {
26524        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
26525        let parsed = parse_cpp_declarations(source, "x.C");
26526        assert!(
26527            parsed
26528                .declarations()
26529                .iter()
26530                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
26531        );
26532    }
26533
26534    /// There is no such thing as a partially nested tag in C: every level of a
26535    /// nested aggregate chain lands at the same enclosing scope.
26536    #[test]
26537    #[hardwon(
26538        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
26539    )]
26540    fn c_file_mints_every_nesting_level_at_file_scope() {
26541        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
26542        let parsed = parse_cpp_declarations(source, "z.c");
26543        let declarations = parsed.declarations();
26544
26545        for tag in ["a", "b", "c"] {
26546            assert!(
26547                declarations
26548                    .iter()
26549                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
26550                "expected a file-scope {tag}, got {declarations:?}"
26551            );
26552        }
26553        assert!(
26554            declarations
26555                .iter()
26556                .all(|unit| !unit.fq_name().contains('$')),
26557            "no level may keep a nested identity, got {declarations:?}"
26558        );
26559        // Each member still belongs to the aggregate that declares it.
26560        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
26561        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
26562        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
26563    }
26564
26565    /// An enum tag is a tag; its enumerators stay members of the enum, which is
26566    /// what makes them ordinary identifiers at the enum's own (file) scope.
26567    #[test]
26568    #[hardwon(
26569        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
26570    )]
26571    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
26572        let source = "struct outer { enum color { RED, GREEN } c; };\n";
26573        let parsed = parse_cpp_declarations(source, "e.c");
26574        let declarations = parsed.declarations();
26575
26576        let color = declarations
26577            .iter()
26578            .find(|unit| unit.is_class() && unit.fq_name() == "color")
26579            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
26580        assert!(
26581            declarations
26582                .iter()
26583                .all(|unit| unit.fq_name() != "outer$color")
26584        );
26585        for enumerator in ["color.RED", "color.GREEN"] {
26586            assert!(
26587                declarations.iter().any(|unit| unit.fq_name() == enumerator),
26588                "expected {enumerator}, got {declarations:?}"
26589            );
26590        }
26591        let children = parsed
26592            .children
26593            .get(color)
26594            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
26595        assert!(
26596            ["color.RED", "color.GREEN"]
26597                .iter()
26598                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
26599            "enumerators must hang off their enum: {children:?}"
26600        );
26601    }
26602
26603    #[test]
26604    #[hardwon(
26605        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
26606    )]
26607    fn c_file_mints_member_list_union_at_file_scope() {
26608        let source = "struct outer { union inner { int a; float b; } item; };\n";
26609        let parsed = parse_cpp_declarations(source, "u.c");
26610        let declarations = parsed.declarations();
26611        assert!(
26612            declarations
26613                .iter()
26614                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
26615            "expected a file-scope inner union, got {declarations:?}"
26616        );
26617        assert!(
26618            declarations
26619                .iter()
26620                .all(|unit| unit.fq_name() != "outer$inner")
26621        );
26622        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
26623        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
26624    }
26625
26626    /// A tag declared in a namespace member list is not a file-scope tag: the
26627    /// nearest enclosing non-aggregate scope is the namespace.
26628    #[test]
26629    #[hardwon(
26630        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
26631    )]
26632    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
26633        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
26634        let parsed = parse_cpp_declarations(source, "n.c");
26635        let declarations = parsed.declarations();
26636        let inner = declarations
26637            .iter()
26638            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
26639            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
26640        assert_eq!(inner.package_name(), "ns");
26641        assert!(
26642            declarations
26643                .iter()
26644                .all(|unit| unit.fq_name() != "ns.outer$inner")
26645        );
26646    }
26647
26648    /// Pins today's treatment of a tag declared inside a function body: the
26649    /// declaration walk does not descend into statement bodies, so no unit is
26650    /// minted for it in either dialect. C block scope is out of scope for the
26651    /// dialect change, and this test proves the change did not disturb it.
26652    #[test]
26653    #[hardwon(
26654        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
26655    )]
26656    fn function_local_tags_are_unchanged_in_both_dialects() {
26657        let source =
26658            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
26659        for name in ["y.c", "y.cpp"] {
26660            let parsed = parse_cpp_declarations(source, name);
26661            let declarations = parsed.declarations();
26662            assert!(
26663                declarations
26664                    .iter()
26665                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
26666                "{name}: {declarations:?}"
26667            );
26668            for tag in ["localtag", "deeper", "localtag$deeper"] {
26669                assert!(
26670                    declarations.iter().all(|unit| unit.fq_name() != tag),
26671                    "{name} must not mint {tag}, got {declarations:?}"
26672                );
26673            }
26674        }
26675    }
26676
26677    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
26678    /// re-scope: the typedef name is identical in both dialects.
26679    #[test]
26680    #[hardwon(
26681        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
26682    )]
26683    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
26684        let source = "typedef struct { int v; } T;\n";
26685        for name in ["t.c", "t.cpp"] {
26686            let parsed = parse_cpp_declarations(source, name);
26687            let declarations = parsed.declarations();
26688            assert!(
26689                declarations
26690                    .iter()
26691                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
26692                "{name}: {declarations:?}"
26693            );
26694        }
26695    }
26696
26697    /// The same principle for every anonymous aggregate the walk indexes, not
26698    /// just the typedef one: a promoted anonymous union in a member list, an
26699    /// anonymous struct given a receiver type by its declarator, a
26700    /// function-local aggregate, and one inside an inline member function
26701    /// body. The C++ reading used to mint none of these, so a pure C++
26702    /// workspace indexed no members at all (#3301).
26703    #[test]
26704    #[hardwon(
26705        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
26706    )]
26707    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
26708        let sources = [
26709            "struct outer { union { int a; float b; }; };\n",
26710            "struct outer { struct { int *ops; } sock; };\n",
26711            "void run(void) { struct { int v; } item; }\n",
26712            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
26713        ];
26714        for source in sources {
26715            let names = |name: &str| {
26716                parse_cpp_declarations(source, name)
26717                    .declarations()
26718                    .iter()
26719                    .map(|unit| unit.fq_name())
26720                    .collect::<std::collections::BTreeSet<_>>()
26721            };
26722            let c_names = names("a.c");
26723            let cpp_names = names("a.cpp");
26724            assert_eq!(
26725                c_names, cpp_names,
26726                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
26727            );
26728            assert!(
26729                c_names
26730                    .iter()
26731                    .any(|name| name.contains('.') && name != "outer"),
26732                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
26733            );
26734        }
26735    }
26736
26737    #[test]
26738    #[hardwon(
26739        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
26740    )]
26741    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
26742        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
26743        let parsed = parse_cpp_declarations(source, "socket.c");
26744        let declarations = parsed.declarations();
26745        assert_eq!(
26746            declarations
26747                .iter()
26748                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
26749                .count(),
26750            1,
26751            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
26752        );
26753        for expected in [
26754            "PAL_HANDLE",
26755            "PAL_HANDLE.sock",
26756            "PAL_HANDLE$sock",
26757            "PAL_HANDLE$sock.ops",
26758        ] {
26759            assert!(
26760                declarations.iter().any(|unit| unit.fq_name() == expected),
26761                "expected {expected}, got {declarations:?}"
26762            );
26763        }
26764    }
26765
26766    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
26767    /// every (node, class-like name) pair a tree offers, and require the same
26768    /// answer from both.
26769    ///
26770    /// The release build has no `debug_assertions` agreement check, so this is
26771    /// what pins the two together there.  Both query orders are exercised:
26772    /// document order is what the walk does, and reverse order proves that a
26773    /// question about an earlier byte than one already answered is still
26774    /// filtered back to its own prefix rather than answered from the wider
26775    /// fold.
26776    ///
26777    /// Returns how many questions were answered with a namespace, so a fixture
26778    /// can assert it actually reached the path (#2754).
26779    fn namespace_forward_scan_agreement(source: &str) -> usize {
26780        let mut parser = tree_sitter::Parser::new();
26781        parser
26782            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26783            .unwrap();
26784        let tree = parser.parse(source, None).unwrap();
26785        let root = tree.root_node();
26786        let ancestry = ParentIndex::new(root);
26787
26788        let mut nodes = Vec::new();
26789        let mut names = std::collections::BTreeSet::new();
26790        let mut cursor = root.walk();
26791        let mut stack = vec![root];
26792        while let Some(node) = stack.pop() {
26793            if matches!(
26794                node.kind(),
26795                "class_specifier" | "struct_specifier" | "union_specifier"
26796            ) && let Some(name) = class_like_name(node, source, &ancestry)
26797            {
26798                names.insert(name);
26799            }
26800            nodes.push(node);
26801            stack.extend(node.named_children(&mut cursor));
26802        }
26803        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
26804        assert!(!names.is_empty(), "fixture declares no class-like name");
26805
26806        let mut answered = 0usize;
26807        for reversed in [false, true] {
26808            let mut scan = CppNamespaceForwardScan::default();
26809            let ordered: Vec<_> = if reversed {
26810                nodes.iter().rev().copied().collect()
26811            } else {
26812                nodes.clone()
26813            };
26814            answered = 0;
26815            for node in ordered {
26816                for name in &names {
26817                    scan.advance_to(root, node.start_byte(), source, &ancestry);
26818                    let carried = scan.unique_earlier_forward(name, node);
26819                    assert_eq!(
26820                        carried,
26821                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
26822                        "carried-forward scan and prefix scan disagree about {name} at \
26823                         {} node starting at byte {} (reversed order: {reversed})",
26824                        node.kind(),
26825                        node.start_byte()
26826                    );
26827                    answered += usize::from(carried.is_some());
26828                }
26829            }
26830        }
26831        answered
26832    }
26833
26834    /// A malformed namespace whose forward declarations are the only identity
26835    /// signal left for the class definitions tree-sitter pushed out to file
26836    /// scope.  Both recovered classes open the guard; only the first one is
26837    /// separated from the namespace by nothing but recovery trivia, so only the
26838    /// first one borrows.  The carried-forward scan has to reproduce both
26839    /// answers.
26840    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
26841namespace ns {
26842class Widget;
26843class Gadget;
26844int x = ;
26845}
26846class API Widget {
26847public:
26848    void first();
26849};
26850class API Gadget {
26851public:
26852    void second();
26853};
26854"#;
26855
26856    #[test]
26857    #[hardwon(
26858        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
26859    )]
26860    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
26861        assert!(
26862            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
26863            "the fixture must actually reach the namespace-borrow path"
26864        );
26865
26866        // Nothing here may be answered, and the two paths have to agree about
26867        // that too: a clean namespace is not an identity proof, two forwards of
26868        // one name are ambiguous rather than a guess, and a forward inside a
26869        // function body is not at namespace scope.
26870        for source in [
26871            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
26872            r#"#define API
26873namespace ns {
26874class Widget;
26875class Widget;
26876int x = ;
26877}
26878class API Widget {
26879public:
26880    void method();
26881};
26882"#,
26883            r#"#define API
26884namespace ns {
26885void host() {
26886    class Widget;
26887}
26888int x = ;
26889}
26890class API Widget {
26891public:
26892    void method();
26893};
26894"#,
26895        ] {
26896            assert_eq!(
26897                namespace_forward_scan_agreement(source),
26898                0,
26899                "no borrow is justified here: {source}"
26900            );
26901        }
26902    }
26903
26904    /// Drive the region reparse and the whitespace-padded reparse it replaced
26905    /// over the same region and require identical trees.
26906    ///
26907    /// The release build has no `debug_assertions` agreement check, so this is
26908    /// what pins the two together there. Each body is reparsed at its own
26909    /// offset and again after a long prefix, because the prefix is the whole
26910    /// difference between the two techniques: the padded parse lexes it as
26911    /// whitespace, the included-range parse never sees it, and the tree has to
26912    /// come out the same either way (#2788).
26913    fn fragmented_class_reparse_agreement(body: &str) {
26914        for prefix in [
26915            String::new(),
26916            "// leading comment\n".to_string(),
26917            // A body starts just after its class head's `{`, which is normally
26918            // mid-line: the padded parse then has spaces before the region on
26919            // the region's own line, and the included-range parse has nothing
26920            // at all before it.
26921            "class Widget : public Base { ".to_string(),
26922            "namespace filler {\n".to_string()
26923                + &"struct Filler { int member; };\n".repeat(200)
26924                + "}\n",
26925            "namespace filler {\n".to_string()
26926                + &"struct Filler { int member; };\n".repeat(200)
26927                + "}\nclass Widget : public Base { ",
26928        ] {
26929            let source = format!("{prefix}{body}");
26930            let start = prefix.len();
26931            let end = source.len();
26932            let region = cpp_reparse_fragmented_class_body(&source, start, end)
26933                .expect("the region reparse must produce a tree");
26934            let padded = cpp_reparse_padded_class_body(&source, start, end)
26935                .expect("the padded reparse must produce a tree");
26936            assert_eq!(
26937                cpp_tree_shape(&region),
26938                cpp_tree_shape(&padded),
26939                "region and padded reparse disagree at offset {start} of {end} bytes"
26940            );
26941            assert_eq!(
26942                region.root_node().start_byte(),
26943                start,
26944                "the reparsed region keeps its original offsets"
26945            );
26946        }
26947    }
26948
26949    #[test]
26950    #[hardwon(
26951        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
26952    )]
26953    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
26954        // A conditional immediately after an access label: the shape the padded
26955        // technique was kept for, because the directive and its macro name
26956        // become an ERROR plus the following declaration's apparent type.
26957        fragmented_class_reparse_agreement(
26958            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
26959        );
26960        fragmented_class_reparse_agreement(
26961            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
26962        );
26963        // The merged inline constructor and the nested fragmented bodies the
26964        // #938 recovery reads out of a reparse.
26965        fragmented_class_reparse_agreement(
26966            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
26967        );
26968        fragmented_class_reparse_agreement(
26969            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
26970        );
26971        // A body that is not member-shaped at all still has to reparse the same
26972        // way, because the admission gate reads the tree to reject it.
26973        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
26974    }
26975
26976    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
26977        let mut siblings = Vec::new();
26978        let mut anchor = node;
26979        while let Some(parent) = anchor.parent() {
26980            let at_translation_unit = parent.kind() == "translation_unit";
26981            let mut sibling = anchor.next_named_sibling();
26982            while let Some(current) = sibling {
26983                if at_translation_unit
26984                    && (current.kind() == "namespace_definition"
26985                        || (current.kind() == "function_definition"
26986                            && first_class_like_child(current).is_some()))
26987                {
26988                    return siblings;
26989                }
26990                siblings.push(current);
26991                if cpp_is_stray_close_brace(current, source) {
26992                    if let Some(semicolon) = current
26993                        .next_named_sibling()
26994                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
26995                    {
26996                        siblings.push(semicolon);
26997                    }
26998                    return siblings;
26999                }
27000                if current.start_byte() >= node.end_byte()
27001                    && matches!(current.kind(), "ERROR" | "labeled_statement")
27002                    && cpp_nested_stray_close_brace(current, source).is_some()
27003                {
27004                    return siblings;
27005                }
27006                sibling = current.next_named_sibling();
27007            }
27008            anchor = parent;
27009        }
27010        siblings
27011    }
27012
27013    #[test]
27014    #[hardwon(
27015        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
27016    )]
27017    fn following_named_siblings_cursor_matches_native_traversal() {
27018        // The damaged #2924 shape scatters members across container siblings,
27019        // and the anonymous punctuation forces the cursor to filter children
27020        // exactly as Node::next_named_sibling does.
27021        let source = r#"
27022namespace api {
27023class PROJECT_API(2, 0) Widget : public virtual Base {
27024  public:
27025    Widget() {}
27026    void first();
27027};
27028enum After { Value };
27029}
27030"#;
27031        let mut parser = tree_sitter::Parser::new();
27032        parser
27033            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27034            .unwrap();
27035        let tree = parser.parse(source, None).unwrap();
27036        let root = tree.root_node();
27037        assert!(root.has_error(), "fixture must remain damaged");
27038
27039        let ancestry = ParentIndex::new(root);
27040        let mut stack = vec![root];
27041        let mut checked = 0;
27042        let mut damaged = 0;
27043        let mut anonymous = 0;
27044        let mut nonempty_sequences = 0;
27045        let mut named_then_anonymous = 0;
27046        while let Some(node) = stack.pop() {
27047            let expected = native_following_named_siblings(node, source)
27048                .iter()
27049                .map(Node::id)
27050                .collect::<Vec<_>>();
27051            let actual = cpp_following_named_siblings(node, source, &ancestry)
27052                .iter()
27053                .map(Node::id)
27054                .collect::<Vec<_>>();
27055            assert_eq!(
27056                actual,
27057                expected,
27058                "cursor traversal diverged at byte {}",
27059                node.start_byte()
27060            );
27061
27062            let mut cursor = node.walk();
27063            let children = node.children(&mut cursor).collect::<Vec<_>>();
27064            stack.extend(children.iter().copied());
27065            checked += 1;
27066            damaged += usize::from(node.is_error() || node.is_missing());
27067            anonymous += usize::from(!node.is_named());
27068            nonempty_sequences += usize::from(!actual.is_empty());
27069            named_then_anonymous += usize::from(
27070                children
27071                    .windows(2)
27072                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
27073            );
27074        }
27075        assert!(checked > 0, "fixture must provide nodes to compare");
27076        assert!(damaged > 0, "fixture must cover damaged nodes");
27077        assert!(anonymous > 0, "fixture must cover anonymous nodes");
27078        assert!(
27079            nonempty_sequences > 0,
27080            "fixture must exercise following named siblings"
27081        );
27082        assert!(
27083            named_then_anonymous > 0,
27084            "fixture must exercise anonymous nodes following a named anchor"
27085        );
27086    }
27087
27088    /// A header shaped like the generated ones this walk is slow on: many
27089    /// enums, classes whose members share the enums' names, nested enums, an
27090    /// ownerless enumerator, and namespaced repeats of all of it.
27091    fn many_enums_and_mixed_declarations() -> String {
27092        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
27093        for index in 0..40 {
27094            let _ = write!(
27095                source,
27096                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
27097                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
27098                 class Color{index}Like {{ public: int member{index}; }};\n"
27099            );
27100        }
27101        source.push_str("namespace outer {\n");
27102        for index in 0..20 {
27103            let _ = write!(
27104                source,
27105                "enum Shade{index} {{ DARK{index} }};\n\
27106                 struct Shade{index}Holder {{ int field{index}; }};\n"
27107            );
27108        }
27109        source.push_str("}\n");
27110        source
27111    }
27112
27113    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
27114    /// every question a fixture's declarations can ask, and require the same
27115    /// answer from both.
27116    ///
27117    /// The release build has no `debug_assertions` agreement check, so this is
27118    /// what pins the two together there. The index is fed one declaration at a
27119    /// time and every question is re-asked after each one, which is what proves
27120    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
27121    /// test. Each of the fixture's own fields is also fed in restated as a
27122    /// declaration of another file: the scan ignores those because it asks
27123    /// about the asking unit's own source, and the index has to ignore them for
27124    /// the same reason.
27125    ///
27126    /// Returns how many questions the fixture answered `true`, so a caller can
27127    /// assert that it actually reached the path (#2786).
27128    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
27129        let parsed = parse_cpp_declarations(source, name);
27130        let file = ProjectFile::new(std::env::temp_dir(), name);
27131        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
27132
27133        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
27134        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
27135
27136        let foreign: Vec<CodeUnit> = declarations
27137            .iter()
27138            .filter(|unit| unit.kind() == CodeUnitType::Field)
27139            .map(|unit| {
27140                CodeUnit::new_fq(
27141                    elsewhere.clone(),
27142                    unit.kind(),
27143                    unit.package_name().to_string(),
27144                    unit.short_name().to_string(),
27145                    unit.fq().clone(),
27146                )
27147            })
27148            .collect();
27149
27150        // One owner chain deeper than any C++ short name reaches today
27151        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
27152        // the member). The scan asks `starts_with("owner.")`, so a field like
27153        // this answers for every owner in its chain, and the index has to
27154        // record every one of them rather than only the innermost.
27155        let mut packages: Vec<String> = declarations
27156            .iter()
27157            .map(|unit| unit.package_name().to_string())
27158            .collect();
27159        packages.push(String::new());
27160        packages.sort();
27161        packages.dedup();
27162        let deeper: Vec<CodeUnit> = packages
27163            .iter()
27164            .map(|package_name| {
27165                CodeUnit::new_fq(
27166                    file.clone(),
27167                    CodeUnitType::Field,
27168                    package_name.clone(),
27169                    "SynthOwner.middle.leaf".to_string(),
27170                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
27171                )
27172            })
27173            .collect();
27174
27175        // Every (package, owner) pair anything could ask about: each unit's own
27176        // short name, each dotted prefix of it, and the empty owner an
27177        // anonymous enum asks with (#2140).
27178        let mut questions: Vec<(String, String)> = Vec::new();
27179        for unit in declarations.iter().chain(deeper.iter()) {
27180            let package_name = unit.package_name().to_string();
27181            let short_name = unit.short_name();
27182            questions.push((package_name.clone(), short_name.to_string()));
27183            questions.push((package_name.clone(), String::new()));
27184            for (offset, _) in short_name.match_indices('.') {
27185                questions.push((package_name.clone(), short_name[..offset].to_string()));
27186            }
27187        }
27188        questions.sort();
27189        questions.dedup();
27190
27191        let mut index = CppFieldOwnerIndex::default();
27192        let mut recorded: Vec<&CodeUnit> = Vec::new();
27193        let mut answered = 0usize;
27194        for unit in foreign
27195            .iter()
27196            .chain(declarations.iter())
27197            .chain(deeper.iter())
27198        {
27199            index.record(unit, &file);
27200            recorded.push(unit);
27201            for (package_name, owner_short_name) in &questions {
27202                let carried = index.owns_fields(package_name, owner_short_name);
27203                assert_eq!(
27204                    carried,
27205                    cpp_declarations_hold_owned_fields(
27206                        recorded.iter().copied(),
27207                        &file,
27208                        package_name,
27209                        owner_short_name
27210                    ),
27211                    "the carried field index and the declaration scan disagree about \
27212                     {package_name:?}/{owner_short_name:?} after recording {}",
27213                    unit.fq_name()
27214                );
27215                answered += usize::from(carried);
27216            }
27217        }
27218
27219        // The whole-set build the first question performs must land on the same
27220        // index the incremental record built.
27221        let rebuilt = CppFieldOwnerIndex::of(
27222            foreign
27223                .iter()
27224                .chain(declarations.iter())
27225                .chain(deeper.iter()),
27226            &file,
27227        );
27228        for (package_name, owner_short_name) in &questions {
27229            assert_eq!(
27230                rebuilt.owns_fields(package_name, owner_short_name),
27231                index.owns_fields(package_name, owner_short_name),
27232                "a rebuilt index must answer what the incremental one answers for \
27233                 {package_name:?}/{owner_short_name:?}"
27234            );
27235        }
27236        answered
27237    }
27238
27239    /// The one thing the field index cannot absorb by addition: a deferred
27240    /// replacement of a declaration that owns children removes those children.
27241    ///
27242    /// The first enum builds the index, the struct records `Color.RED` into it,
27243    /// the body-less second `Color` replaces the first and takes `Color.RED`
27244    /// with it, and the last enum then asks about owner `Color`. An index that
27245    /// survived that removal answers `true` where the declarations say `false`,
27246    /// which is exactly what the in-walk agreement assertion catches (#2786).
27247    #[test]
27248    #[hardwon(
27249        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
27250    )]
27251    fn a_replacement_that_removes_children_drops_the_field_index() {
27252        let source =
27253            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
27254        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
27255        let mut names: Vec<_> = parsed
27256            .declarations()
27257            .iter()
27258            .map(|unit| unit.fq_name())
27259            .collect();
27260        names.sort();
27261        assert_eq!(
27262            names,
27263            vec![
27264                "Color".to_string(),
27265                "First".to_string(),
27266                "First.A".to_string()
27267            ],
27268            "the replaced Color owns no field any more"
27269        );
27270    }
27271
27272    /// A recovery that re-declares what the file already declared mints
27273    /// nothing.
27274    ///
27275    /// The reparse walk replaces the outer `Widget`, which removes its method,
27276    /// and then re-creates that method from the region. Creation alone would
27277    /// call the method recovered; it was there before the recovery opened, so
27278    /// the recovered set is empty and only the region's own reparse window is
27279    /// recorded (#2787).
27280    #[test]
27281    #[hardwon(
27282        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
27283    )]
27284    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
27285        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
27286                      BEGIN_NS\n\
27287                      namespace demo { struct Widget { void doWork(); }; }\n\
27288                      END_NS\n";
27289        let parsed = parse_cpp_declarations(source, "restored.cpp");
27290        let recovered: Vec<String> = parsed
27291            .materialization_records
27292            .iter()
27293            .filter_map(|record| match record {
27294                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
27295                _ => None,
27296            })
27297            .collect();
27298        assert!(
27299            recovered.is_empty(),
27300            "the region declares nothing the file did not already declare: {recovered:?}"
27301        );
27302        let mut names: Vec<String> = parsed
27303            .declarations()
27304            .iter()
27305            .map(|unit| unit.fq_name())
27306            .collect();
27307        names.sort();
27308        assert_eq!(
27309            names,
27310            vec![
27311                "demo".to_string(),
27312                "demo.Widget".to_string(),
27313                "demo.Widget.doWork".to_string(),
27314            ]
27315        );
27316    }
27317
27318    /// Four macro-sentinel recoveries in one file (#941). Each one must record
27319    /// exactly the declarations it minted -- not the ones an earlier recovery
27320    /// minted, and not the file's other declarations -- in start-byte order,
27321    /// and each record must carry its own reparse window (#2787).
27322    #[test]
27323    #[hardwon(
27324        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
27325    )]
27326    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
27327        let mut source = String::new();
27328        for index in 0..4 {
27329            let _ = write!(
27330                source,
27331                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
27332            );
27333        }
27334        source.push_str("void outside() {}\n");
27335        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
27336
27337        let recovered: Vec<(String, (usize, usize))> = parsed
27338            .materialization_records
27339            .iter()
27340            .filter_map(|record| match record {
27341                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
27342                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
27343                }
27344                _ => None,
27345            })
27346            .collect();
27347
27348        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
27349        for index in 0..4 {
27350            // The window the reparse covers: everything after the opening
27351            // sentinel token up to the newline before the closing one.
27352            let region = format!("namespace demo{index}");
27353            let region_start = source.find(&region).expect("each region is in the source");
27354            let start = source[..region_start]
27355                .rfind("BEGIN_NS")
27356                .expect("each region opens with a sentinel")
27357                + "BEGIN_NS".len();
27358            let end = start
27359                + source[start..]
27360                    .find("END_NS")
27361                    .expect("each region closes with a sentinel")
27362                - 1;
27363            let window = (start, end);
27364            for name in [
27365                format!("demo{index}"),
27366                format!("demo{index}.Widget{index}"),
27367                format!("demo{index}.Widget{index}.doWork{index}"),
27368            ] {
27369                expected.push((name, window));
27370            }
27371        }
27372        assert_eq!(
27373            recovered, expected,
27374            "each recovery records its own minted declarations, in order"
27375        );
27376        assert!(
27377            parsed
27378                .declarations()
27379                .iter()
27380                .any(|unit| unit.fq_name() == "outside"),
27381            "the declaration outside every region stays parsed and unrecovered"
27382        );
27383    }
27384
27385    #[test]
27386    #[hardwon(
27387        "69c2e1f7e41c: C++ declaration recovery regressed for carried forward field index answers what the declaration scan answers"
27388    )]
27389    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
27390        assert!(
27391            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
27392            "the fixture must actually own fields"
27393        );
27394
27395        // The shapes that make the two implementations diverge if the index
27396        // records the wrong keys: a nested enum's owner is its whole dotted
27397        // chain, a class named like an enum owns fields under that same name, a
27398        // `$` in a short name is an owner separator the dotted prefix rule must
27399        // not split on, and the same enum name in two namespaces is two owners.
27400        for (source, name) in [
27401            ("struct S { enum E { V }; };\n", "nested.hpp"),
27402            (
27403                "enum Color { RED };\nstruct Color { int RED; };\n",
27404                "class-like.c",
27405            ),
27406            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
27407            (
27408                "enum E { V };\nnamespace ns { enum E { V }; }\n",
27409                "repeated.hpp",
27410            ),
27411            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
27412        ] {
27413            field_owner_index_agreement(source, name);
27414        }
27415    }
27416
27417    /// `blocks` copies of one plain class-with-methods template. The class the
27418    /// question is about is always the first one, so the only thing that
27419    /// changes between two of these sources is how much unrelated tree
27420    /// surrounds it.
27421    fn repeated_class_blocks(blocks: usize) -> String {
27422        let mut source = String::from("namespace demo {\n");
27423        for index in 0..blocks {
27424            let _ = write!(
27425                source,
27426                "\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"
27427            );
27428        }
27429        source.push_str("\n}\n");
27430        source
27431    }
27432
27433    /// #1496: asking whether a recovered class shape claims one range must cost
27434    /// the path to that range, not a pass over the whole translation unit.
27435    ///
27436    /// The C++ inverse scan asks this once per candidate type reference, so a
27437    /// whole-tree walk makes one file's scan quadratic in its own size. Both
27438    /// sources here answer `None` -- these are plain classes that no recovery
27439    /// shape claims -- which is exactly the case that used to pay full price.
27440    #[test]
27441    #[hardwon(
27442        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
27443    )]
27444    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
27445        let mut answers = Vec::new();
27446        let mut visits = Vec::new();
27447        let mut node_counts = Vec::new();
27448        for blocks in [200usize, 400] {
27449            let source = repeated_class_blocks(blocks);
27450            let mut parser = tree_sitter::Parser::new();
27451            parser
27452                .set_language(&tree_sitter_cpp::LANGUAGE.into())
27453                .unwrap();
27454            let tree = parser.parse(&source, None).unwrap();
27455            let start_byte = source.find("class Widget0 ").expect("first class");
27456            let end_byte = start_byte
27457                + source[start_byte..]
27458                    .find("};")
27459                    .expect("first class terminator")
27460                + "};".len();
27461            let range = Range {
27462                start_byte,
27463                end_byte,
27464                start_line: 0,
27465                end_line: 0,
27466            };
27467            reset_recovered_class_body_node_visits_for_test();
27468            let recovered_export_classes =
27469                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
27470            answers.push(recovered_class_body_at(
27471                &recovered_export_classes,
27472                tree.root_node(),
27473                &source,
27474                "Widget0",
27475                &range,
27476            ));
27477            visits.push(recovered_class_body_node_visits_for_test());
27478            let mut nodes = 0usize;
27479            let mut stack = vec![tree.root_node()];
27480            while let Some(node) = stack.pop() {
27481                nodes += 1;
27482                let mut cursor = node.walk();
27483                stack.extend(node.named_children(&mut cursor));
27484            }
27485            node_counts.push(nodes);
27486        }
27487
27488        assert_eq!(
27489            answers,
27490            vec![None, None],
27491            "no recovered shape claims a plain class"
27492        );
27493        assert_eq!(
27494            visits[0], visits[1],
27495            "the walk must follow the range's own path, so doubling the unrelated \
27496             classes must not change the node count: {visits:?} over trees of \
27497             {node_counts:?} nodes"
27498        );
27499        assert!(
27500            visits[1] * 20 < node_counts[1],
27501            "the walk must stay far below one pass over the tree: {visits:?} over \
27502             trees of {node_counts:?} nodes"
27503        );
27504    }
27505
27506    #[test]
27507    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
27508    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
27509        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
27510        let mut parser = tree_sitter::Parser::new();
27511        parser
27512            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27513            .expect("C++ grammar");
27514        let tree = parser.parse(source, None).expect("fixture tree");
27515        let mut stack = vec![tree.root_node()];
27516        let mut recovered = None;
27517        while let Some(node) = stack.pop() {
27518            if node.kind() == "field_declaration"
27519                && let Some(field) = recovered_function_like_field_declarator(node, source)
27520            {
27521                recovered = Some((node, field.name));
27522                break;
27523            }
27524            let mut cursor = node.walk();
27525            stack.extend(node.named_children(&mut cursor));
27526        }
27527        let (declaration, name) = recovered.unwrap_or_else(|| {
27528            panic!(
27529                "pointer-wrapped macro field was not recovered: {}",
27530                tree.root_node().to_sexp()
27531            )
27532        });
27533        assert_eq!(node_text(name, source), "handshake");
27534        let recovered =
27535            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
27536        assert_eq!(recovered.pointer_depth(), 1);
27537        assert_eq!(
27538            render_cpp_field_signature(declaration, name, source),
27539            "struct handshake * handshake;"
27540        );
27541    }
27542
27543    #[test]
27544    #[hardwon(
27545        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27546    )]
27547    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
27548        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
27549        let mut parser = tree_sitter::Parser::new();
27550        parser
27551            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27552            .expect("C++ grammar");
27553        let tree = parser.parse(source, None).expect("fixture tree");
27554        // The macro-declaration grammar reads `PyObject_HEAD` as a
27555        // `macro_modifier`, so the member arrives as an ordinary clean field
27556        // declaration rather than the recovery shape the ERROR-based reader
27557        // exists for.
27558        let mut stack = vec![tree.root_node()];
27559        let mut declaration = None;
27560        while let Some(node) = stack.pop() {
27561            if node.kind() == "field_declaration" {
27562                declaration = Some(node);
27563                break;
27564            }
27565            let mut cursor = node.walk();
27566            stack.extend(node.named_children(&mut cursor));
27567        }
27568        let declaration = declaration.unwrap_or_else(|| {
27569            panic!(
27570                "the pointer field must arrive as an ordinary field declaration: {}",
27571                tree.root_node().to_sexp()
27572            )
27573        });
27574        let declarator = declaration
27575            .child_by_field_name("declarator")
27576            .expect("pointer field declarator");
27577        assert_eq!(node_text(declarator, source), "*image");
27578        assert_eq!(
27579            render_cpp_field_signature(declaration, declarator, source),
27580            "PyObject_HEAD ImagingObject * image;",
27581            "the macro marker is retained as modifier provenance, the same \
27582             treatment an attribute or storage-class specifier gets"
27583        );
27584        assert_eq!(
27585            crate::source_facts::capture_field_type(declaration, "image", source),
27586            (
27587                Some(
27588                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
27589                        type_text: "ImagingObject".to_owned(),
27590                        indirection: 1,
27591                        binds_indirectly: true,
27592                        template_arguments: None,
27593                    }
27594                ),
27595                false,
27596            ),
27597            "canonical field facts must use the declared type, not the macro token"
27598        );
27599    }
27600
27601    #[test]
27602    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
27603        let source = "class outer { class inner { int v; }; };\n";
27604        let c_parsed = parse_cpp_declarations(source, "k.c");
27605        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
27606        let c_declarations = c_parsed.declarations();
27607        let cpp_declarations = cpp_parsed.declarations();
27608        assert!(
27609            c_declarations
27610                .iter()
27611                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27612            "{c_declarations:?}"
27613        );
27614        assert_eq!(
27615            c_declarations
27616                .iter()
27617                .map(|unit| unit.fq_name())
27618                .collect::<std::collections::BTreeSet<_>>(),
27619            cpp_declarations
27620                .iter()
27621                .map(|unit| unit.fq_name())
27622                .collect::<std::collections::BTreeSet<_>>()
27623        );
27624    }
27625    #[test]
27626    fn callable_metadata_records_c_receiver_contracts_structurally() {
27627        const SOURCE: &str = concat!(
27628            "static long accumulate(const long *lines, int count) { return count; }\n",
27629            "\n",
27630            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
27631        );
27632        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
27633
27634        assert_eq!(
27635            recorded_callable_modifiers(&parsed),
27636            vec![
27637                "accumulate decl_only=false static=false constructor=false \
27638                 visibility=Unknown"
27639                    .to_string(),
27640                "total decl_only=false static=false constructor=false visibility=Unknown"
27641                    .to_string(),
27642            ],
27643        );
27644    }
27645    #[test]
27646    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
27647        const SOURCE: &str = concat!(
27648            "namespace shop {\n",
27649            "\n",
27650            "class Client {\n",
27651            "public:\n",
27652            "    Client();\n",
27653            "    void send(int order);\n",
27654            "    static int count();\n",
27655            "protected:\n",
27656            "    virtual int audit(int n) const { return n; }\n",
27657            "private:\n",
27658            "    int tally(int n) { return n; }\n",
27659            "};\n",
27660            "\n",
27661            "struct Ledger {\n",
27662            "    void record(int n) { (void)n; }\n",
27663            "};\n",
27664            "\n",
27665            "Client::Client() {}\n",
27666            "void Client::send(int order) { (void)order; }\n",
27667            "int Client::count() { return 0; }\n",
27668            "\n",
27669            "int total(int order) { return order; }\n",
27670            "\n",
27671            "}\n",
27672        );
27673        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
27674
27675        assert_eq!(
27676            recorded_callable_modifiers(&parsed),
27677            vec![
27678                "shop.Client.Client decl_only=false static=false constructor=true \
27679                 visibility=Unknown"
27680                    .to_string(),
27681                "shop.Client.Client decl_only=true static=false constructor=true \
27682                 visibility=Public"
27683                    .to_string(),
27684                "shop.Client.audit decl_only=false static=false constructor=false \
27685                 visibility=Protected"
27686                    .to_string(),
27687                "shop.Client.count decl_only=false static=false constructor=false \
27688                 visibility=Unknown"
27689                    .to_string(),
27690                "shop.Client.count decl_only=true static=true constructor=false \
27691                 visibility=Public"
27692                    .to_string(),
27693                "shop.Client.send decl_only=false static=false constructor=false \
27694                 visibility=Unknown"
27695                    .to_string(),
27696                "shop.Client.send decl_only=true static=false constructor=false \
27697                 visibility=Public"
27698                    .to_string(),
27699                "shop.Client.tally decl_only=false static=false constructor=false \
27700                 visibility=Private"
27701                    .to_string(),
27702                "shop.Ledger.record decl_only=false static=false constructor=false \
27703                 visibility=Public"
27704                    .to_string(),
27705                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
27706                    .to_string(),
27707            ],
27708        );
27709    }
27710}