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;
9use brokk_bifrost_core::analyzer::common::{
10    node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::materialization::{
24    GenerationKind, MaterializationRecord,
25};
26use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
27use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
28use brokk_bifrost_core::hash::{HashMap, HashSet};
29use regex::Regex;
30use tree_sitter::{Node, Parser, Tree};
31
32/// Intern one qualified-name segment in the process-global interner.
33fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
34    segment_interner().intern(text, kind)
35}
36
37/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
38/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
39/// `::` head is exactly the mixed-separator store issue #1163 is about; the
40/// structured form records each namespace component, and the equivalence check
41/// renders it natively (with `::` between adjacent Package segments) so it
42/// round-trips to the legacy string. Splitting the already-joined string here is
43/// the M1 bridge — the legacy strings stay authoritative until M3.
44fn cpp_push_package(fq: &mut FqName, package_name: &str) {
45    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
46        fq.push(cpp_segment(component, SegmentKind::Package));
47    }
48}
49
50/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
51const CPP_PACKAGE_SEPARATOR: &str = "::";
52
53/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
54/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
55/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
56/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
57/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
58/// cpp-specific native rendering rule is needed for this chain.
59fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
60    let mut first = true;
61    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
62    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
63    // entry point, not re-inference of an already-structured name.
64    for component in chain.split('$').filter(|c| !c.is_empty()) {
65        let kind = if first {
66            SegmentKind::Type
67        } else {
68            SegmentKind::Nested
69        };
70        fq.push(cpp_segment(component, kind));
71        first = false;
72    }
73}
74
75/// Structured name for a C++ namespace module: every `::`-separated component is
76/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
77/// `short_name` with an empty `package_name`).
78fn cpp_namespace_fq(full_name: &str) -> FqName {
79    let mut fq = FqName::new();
80    cpp_push_package(&mut fq, full_name);
81    fq
82}
83
84/// The per-level namespace components a `namespace_definition`'s `name` field
85/// declares.
86///
87/// A C++17 nested definition (`namespace a::b::c`) parses as a
88/// `nested_namespace_specifier` whose named children are the per-level
89/// `namespace_identifier`s plus, for three or more levels, a further
90/// `nested_namespace_specifier`; the `::` separators, the optional per-level
91/// `inline`, and the leading global `::` are all anonymous tokens the walk
92/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
93/// segment path as the expanded `namespace a { namespace b { } }` form.
94///
95/// A shape outside that grammar is the deliberately ill-formed source the
96/// diagnostic corpora carry. Those keep their historical single-component
97/// reading of the raw name text, which the caller still joins to the lexical
98/// namespace exactly as before.
99fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
100    let mut components = Vec::new();
101    let mut stack = vec![node];
102    while let Some(current) = stack.pop() {
103        match current.kind() {
104            "namespace_identifier" | "identifier" => {
105                components.push(normalize_cpp_whitespace(node_text(current, source)));
106            }
107            "nested_namespace_specifier" => {
108                for index in (0..current.named_child_count()).rev() {
109                    stack.push(
110                        current
111                            .named_child(index)
112                            .expect("index below the node's own named child count"),
113                    );
114                }
115            }
116            _ => return cpp_raw_namespace_name_components(node, source),
117        }
118    }
119    if components.iter().any(String::is_empty) {
120        return cpp_raw_namespace_name_components(node, source);
121    }
122    components
123}
124
125/// The historical reading of a namespace name node: its whole source text as
126/// one component, with a leading global `::` marker dropped so the caller's
127/// global-scope handling stays the AST boundary rather than a text prefix.
128///
129/// This is the recovery path for source outside the C++ grammar, so the text it
130/// returns can carry a separator that names nothing. react-native-windows
131/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
132/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
133/// trailing separator with no tail. The caller stores this component in
134/// `short_name` and derives the fq by splitting it back apart, so an empty tail
135/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
136/// here, at the one place the malformed text enters, rather than leaving each
137/// consumer to guard (#2353, a variant of #1878).
138fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
139    let start = node
140        .child(0)
141        .filter(|child| !child.is_named() && child.kind() == "::")
142        .map_or(node.start_byte(), |marker| marker.end_byte());
143    let text = normalize_cpp_whitespace(
144        source
145            .get(start..node.end_byte())
146            .expect("namespace name node covers one source range"),
147    );
148    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
149    if text.is_empty() {
150        return Vec::new();
151    }
152    vec![text]
153}
154
155/// Return the named namespace path that structurally encloses `node`.
156///
157/// This intentionally follows namespace AST ancestors rather than inspecting
158/// source text. Anonymous namespaces are not representable in the legacy C++
159/// package field, so a path containing one fails closed.
160fn cpp_lexical_namespace_name<'tree>(
161    node: Node<'tree>,
162    source: &str,
163    ancestry: &ParentIndex<'tree>,
164) -> Option<String> {
165    let mut components = Vec::new();
166    let mut ancestor = ancestry.parent(node);
167    while let Some(current) = ancestor {
168        if current.kind() == "namespace_definition" {
169            let name_node = current.child_by_field_name("name")?;
170            let name = normalize_cpp_whitespace(node_text(name_node, source));
171            if name.is_empty() {
172                return None;
173            }
174            components.push(name);
175        }
176        ancestor = ancestry.parent(current);
177    }
178    if components.is_empty() {
179        return None;
180    }
181    components.reverse();
182    // Same shared empty-component decision as `cpp_push_package`, which splits
183    // this string back apart: an enclosing namespace whose own name node is
184    // malformed contributes a component carrying its own separator (#2353).
185    Some(
186        normalize_joined(
187            &components.join(CPP_PACKAGE_SEPARATOR),
188            CPP_PACKAGE_SEPARATOR,
189        )
190        .into_owned(),
191    )
192}
193
194/// Nested-class `$` join for short names. An anonymous parent class (empty
195/// short_name) contributes no segment: the FqName bridge drops empty
196/// components, so a bare `parent$child` join would desync `short_name` from
197/// the fq and trip the package/short boundary assert in
198/// `CodeUnit::with_signature_and_fq` (#2140).
199fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
200    if parent_short.is_empty() {
201        name.to_string()
202    } else {
203        format!("{parent_short}${name}")
204    }
205}
206
207/// Member `.` join for short names; same anonymous-parent guard as
208/// [`cpp_join_nested_short`] (#2140).
209fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
210    if parent_short.is_empty() {
211        name.to_string()
212    } else {
213        format!("{parent_short}.{name}")
214    }
215}
216
217/// Structural fq for a leaf declaration: the parent unit's fq plus this
218/// declaration's own name as one segment (or the package segments plus the
219/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
220/// chain, so a literal `$` inside a source identifier (Cython template
221/// substitution points, gcc `$`-identifiers) survives instead of corrupting
222/// the chain and tripping the package/short boundary assert (#2140).
223fn cpp_leaf_fq(
224    package_name: &str,
225    parent: Option<&CodeUnit>,
226    name: &str,
227    kind_if_nested: SegmentKind,
228    kind_if_top: SegmentKind,
229) -> FqName {
230    if let Some(parent) = parent {
231        parent
232            .fq()
233            .clone()
234            .with_pushed(cpp_segment(name, kind_if_nested))
235    } else {
236        let mut fq = FqName::new();
237        cpp_push_package(&mut fq, package_name);
238        fq.push(cpp_segment(name, kind_if_top));
239        fq
240    }
241}
242
243/// Structured name for a member unit (function, field, enumerator). The
244/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
245/// the member has an owner, by `.member`; free functions and globals have no
246/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
247/// C++ member names never contain a literal `.`, so the single `.` (if any)
248/// separates the owner chain from the member.
249pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
250    let mut fq = FqName::new();
251    cpp_push_package(&mut fq, package_name);
252    match short_name.rsplit_once('.') {
253        Some((owner_chain, member)) => {
254            cpp_push_type_chain(&mut fq, owner_chain);
255            fq.push(cpp_segment(member, SegmentKind::Member));
256        }
257        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
258    }
259    fq
260}
261
262#[derive(Clone)]
263pub struct ScopeInfo {
264    package_name: String,
265    module: Option<CodeUnit>,
266    class_unit: Option<CodeUnit>,
267    template_signature: Option<String>,
268    template_metadata: Option<CppTemplateMetadata>,
269    declarations_are_fields: bool,
270    recovered_specialization_member_scope: bool,
271    /// Namespace targets of every `using namespace X;` directive lexically
272    /// visible at this point in the file (declaration order), threaded
273    /// forward sibling-by-sibling by the sequential container walk (see
274    /// `CppWork::Siblings`). An out-of-line member definition written as a
275    /// bare `Class::method` at file/namespace scope with no enclosing
276    /// `namespace {}` block (issue #1093, e.g. log4cxx's
277    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
278    /// const { ... }`) has no other structural signal for which namespace
279    /// actually owns `Class`; this is the best-effort candidate list used to
280    /// recover it so the definition's indexed identity matches its header
281    /// declaration's.
282    visible_using_namespaces: Vec<String>,
283}
284
285struct CppContainer<'tree> {
286    node: Node<'tree>,
287    scope: ScopeInfo,
288}
289
290struct CppNodeWork<'tree> {
291    node: Node<'tree>,
292    scope: ScopeInfo,
293}
294
295/// Cursor over one container's remaining named children, processed one at a
296/// time (rather than all at once) so a `using namespace X;` sibling can
297/// update `scope.visible_using_namespaces` for the siblings that follow it,
298/// matching real C++ using-directive semantics. Nested container work is
299/// still pushed and fully drained before the cursor resumes (stack LIFO
300/// order), preserving the original left-to-right visitation order.
301struct CppSiblingsWork<'tree> {
302    children: std::vec::IntoIter<Node<'tree>>,
303    scope: ScopeInfo,
304}
305
306enum CppWork<'tree> {
307    Container(CppContainer<'tree>),
308    Node(CppNodeWork<'tree>),
309    Siblings(CppSiblingsWork<'tree>),
310}
311
312fn class_like_name<'tree>(
313    node: Node<'tree>,
314    source: &str,
315    ancestry: &ParentIndex<'tree>,
316) -> Option<String> {
317    let best = class_like_name_from_children(node, source);
318    if let Some(parent) = ancestry.parent(node)
319        && matches!(
320            parent.kind(),
321            "declaration" | "field_declaration" | "function_definition"
322        )
323        // A class_specifier carrying its own body proves the grammar name is
324        // the real class name: a sibling declarator then declares an object
325        // (`class X {} x;`), never a displaced class name. The gate matters
326        // when the class name is itself an all-caps token (`X`, `API`) --
327        // without it the export-macro re-read below steals the object
328        // declarator's name for the class (#2283). The genuine export-macro
329        // shapes leave the class_specifier bodyless, the same invariant
330        // recover_malformed_exported_multiple_base_class already gates on.
331        && cpp_body_node(node).is_none()
332        && node
333            .child_by_field_name("name")
334            .map(|name_node| {
335                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
336            })
337            .unwrap_or(false)
338        && let Some(recovered) = exported_class_name_from_node(parent, source)
339        && best.as_deref() != Some(recovered.as_str())
340    {
341        return Some(recovered);
342    }
343    best.or_else(|| {
344        node.child_by_field_name("name")
345            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
346            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
347    })
348}
349
350fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
351    let mut grammar_name = None;
352    if let Some(name_node) = node.child_by_field_name("name") {
353        let name = normalize_cpp_whitespace(node_text(name_node, source));
354        if name.is_empty() {
355            return None;
356        }
357        if !cpp_export_macro_token(&name) {
358            return Some(name);
359        }
360        grammar_name = Some(name);
361    }
362
363    let mut best = None;
364    let mut cursor = node.walk();
365    let mut stack = Vec::new();
366    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
367        if matches!(
368            child.kind(),
369            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
370        ) {
371            break;
372        }
373        stack.push(child);
374    }
375
376    while let Some(current) = stack.pop() {
377        if matches!(current.kind(), "type_identifier" | "identifier") {
378            let name = normalize_cpp_whitespace(node_text(current, source));
379            if !name.is_empty() && !cpp_export_macro_token(&name) {
380                best = Some(name);
381            }
382            continue;
383        }
384
385        for index in (0..current.named_child_count()).rev() {
386            if let Some(child) = current.named_child(index) {
387                stack.push(child);
388            }
389        }
390    }
391    best.or(grammar_name)
392}
393
394pub fn cpp_export_macro_token(token: &str) -> bool {
395    token
396        .chars()
397        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
398}
399
400struct RecoveredExportedClass<'tree> {
401    declaration_node: Node<'tree>,
402    name: String,
403    body: Option<Node<'tree>>,
404    raw_supertypes: Option<Vec<String>>,
405    uses_initializer_body: bool,
406    /// Present only for the fragmented multiple-base export shape (issue #938).
407    /// Carries the true class-body byte region -- the members tree-sitter scattered
408    /// out of the recovered node -- so they can be reparsed and re-owned as members
409    /// rather than lost inside the truncated `initializer_list` stand-in.
410    fragmented_body: Option<FragmentedExportBody>,
411}
412
413struct RecoveredFunctionLikeExportClassPair {
414    name: String,
415    range: Range,
416    raw_supertypes: Option<Vec<String>>,
417    fragmented_body: FragmentedExportBody,
418}
419
420struct RecoveredEmbeddedFunctionLikeExportClass {
421    name: String,
422    range: Range,
423    raw_supertypes: Vec<String>,
424    fragmented_body: FragmentedExportBody,
425}
426
427/// The recovered class-body geometry for a fragmented multiple-base export class.
428/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
429/// verbatim for a region reparse (issue #941 machinery) so every recovered member
430/// keeps its exact original byte/line position. `class_range` is the full class
431/// navigation range spanning to the displaced closing brace.
432struct FragmentedExportBody {
433    reparse_start: usize,
434    reparse_end: usize,
435    class_range: Range,
436}
437
438fn recovered_fragmented_export_body(
439    body: Node<'_>,
440    class_range: Range,
441) -> Option<FragmentedExportBody> {
442    let open = body.child(0).filter(|child| child.kind() == "{")?;
443    let close = body
444        .child(body.child_count().saturating_sub(1))
445        .filter(|child| child.kind() == "}" && !child.is_missing());
446    Some(FragmentedExportBody {
447        reparse_start: open.end_byte(),
448        // A zero-width missing `}` contributes no source byte. Keep the whole
449        // body range in that case; subtracting one byte would discard the last
450        // member's semicolon and make the otherwise valid region unsafe to
451        // index. A real close token is excluded by its structured start.
452        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
453        class_range,
454    })
455}
456
457struct DisplacedFragmentNamespaceBoundary<'tree> {
458    class_close: Node<'tree>,
459    class_semicolon: Node<'tree>,
460    namespace_items: Vec<Node<'tree>>,
461}
462
463/// Result of validating a reparsed fragmented class body.  A complete tree can
464/// safely consume the whole region.  A partial tree may contain only the exact
465/// class-named constructor that tree-sitter merged into an access label; its
466/// remaining siblings must stay on the ordinary outer walk.
467enum FragmentedExportMembers {
468    Complete(Tree),
469    ConditionalConstructor(Tree),
470}
471
472#[derive(Clone, Copy)]
473struct DisplacedMacroClassTail {
474    split_index: usize,
475    class_range: Range,
476}
477
478fn recover_exported_class_declaration<'tree>(
479    node: Node<'tree>,
480    source: &str,
481) -> Option<RecoveredExportedClass<'tree>> {
482    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
483        return Some(recovered);
484    }
485
486    let class_node = first_class_like_child(node)?;
487    if let Some(name_node) = class_node.child_by_field_name("name") {
488        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
489        if cpp_export_macro_token(&class_name) {
490            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
491            // Name declarator. Only a bare declarator can be the displaced class name;
492            // wrappers describe an object whose type merely happens to look macro-like.
493            let mut cursor = node.walk();
494            if node
495                .children_by_field_name("declarator", &mut cursor)
496                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
497            {
498                return None;
499            }
500        } else if has_direct_cpp_declarator(node) {
501            return None;
502        }
503    }
504    let name = exported_class_name_from_node(class_node, source)?;
505    Some(RecoveredExportedClass {
506        declaration_node: class_node,
507        name,
508        body: cpp_body_node(class_node),
509        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
510            .then(|| extract_cpp_supertypes(class_node, source)),
511        uses_initializer_body: false,
512        fragmented_body: None,
513    })
514}
515
516fn recover_malformed_exported_base_class<'tree>(
517    node: Node<'tree>,
518    source: &str,
519) -> Option<RecoveredExportedClass<'tree>> {
520    if node.kind() != "declaration" {
521        return None;
522    }
523    let class_node = node.child_by_field_name("type")?;
524    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
525        return None;
526    }
527    let macro_name = class_node
528        .child_by_field_name("name")
529        .and_then(|name| direct_identifier_name(name, source))?;
530    if !cpp_export_macro_token(&macro_name) {
531        return None;
532    }
533
534    let mut named_cursor = node.walk();
535    let mut named = node.named_children(&mut named_cursor);
536    if named
537        .next()
538        .is_none_or(|child| !same_node(child, class_node))
539    {
540        return None;
541    }
542    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
543    if displaced.kind() != "ERROR" {
544        return None;
545    }
546    let name = displaced_exported_class_name(displaced, source)?;
547
548    let remaining = named.collect::<Vec<_>>();
549    let init = *remaining.last()?;
550    if init.kind() != "init_declarator" {
551        return None;
552    }
553    let final_base = init
554        .child_by_field_name("declarator")
555        .and_then(|base| recovered_malformed_base_name(base, source))?;
556    let body = init.child_by_field_name("value")?;
557    // A complete reduction has a real closing brace here. In Chromium's Widget
558    // declaration, tree-sitter instead emits the same direct `}` slot as a
559    // zero-width missing node where the first body macro truncates the prefix.
560    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
561        return None;
562    }
563
564    if remaining[..remaining.len() - 1]
565        .iter()
566        .any(|child| match child.kind() {
567            "qualified_identifier"
568            | "scoped_type_identifier"
569            | "type_identifier"
570            | "identifier" => false,
571            "ERROR" => !is_malformed_inheritance_access(*child, source),
572            _ => true,
573        })
574    {
575        return None;
576    }
577
578    let mut raw_supertypes = Vec::new();
579    for base in &remaining[..remaining.len() - 1] {
580        if base.kind() == "ERROR" {
581            continue;
582        }
583        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
584    }
585    raw_supertypes.push(final_base);
586
587    Some(RecoveredExportedClass {
588        declaration_node: node,
589        name,
590        body: Some(body),
591        raw_supertypes: Some(raw_supertypes),
592        uses_initializer_body: true,
593        fragmented_body: fragmented_export_body_region(node, body, source),
594    })
595}
596
597/// Locate the true class-body region for a fragmented multiple-base export class.
598///
599/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
600/// emits in place of the real class body. Tree-sitter reduces that body in one of
601/// two shapes, both of which lose the members from the recovered node:
602///
603/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
604///   a real closing brace and holds the whole body text inline. The interior between
605///   the braces reparses to the members directly.
606/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
607///   first member with a zero-width MISSING `}`; every later member -- and the real
608///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
609///   siblings. The interior runs from the opening brace to that displaced `}`.
610///
611/// Returns the interior byte range to reparse plus the full class navigation range.
612fn fragmented_export_body_region(
613    node: Node<'_>,
614    body: Node<'_>,
615    source: &str,
616) -> Option<FragmentedExportBody> {
617    let reparse_start = body.start_byte() + 1;
618    let close = direct_close_brace(body)?;
619    if close.end_byte() > close.start_byte() {
620        return Some(FragmentedExportBody {
621            reparse_start,
622            reparse_end: close.start_byte(),
623            class_range: cpp_declaration_range(node),
624        });
625    }
626    // The closing brace was displaced past the recovered node. A balanced nested
627    // class keeps its own braces, so the first lone-`}` sibling is this class's.
628    let mut sibling = node.next_named_sibling();
629    let displaced_close = loop {
630        let Some(current) = sibling else {
631            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
632        };
633        if cpp_is_stray_close_brace(current, source) {
634            break current;
635        }
636        sibling = current.next_named_sibling();
637    };
638    Some(FragmentedExportBody {
639        reparse_start,
640        reparse_end: displaced_close.start_byte(),
641        class_range: Range {
642            start_byte: node.start_byte(),
643            end_byte: displaced_close.end_byte(),
644            start_line: node.start_position().row + 1,
645            end_line: displaced_close.end_position().row + 1,
646        },
647    })
648}
649
650/// Locate the true class-body region for the export-macro class shape that
651/// tree-sitter promotes to a `function_definition`.
652///
653/// In this shape the synthetic function body closes at the first inline
654/// method, while the class's real members continue as root-level siblings until
655/// a stray `}` followed by the displaced class `;`. Reparse the complete
656/// interior so those siblings are visited with the recovered class scope.
657fn fragmented_export_function_body_region(
658    node: Node<'_>,
659    body: Node<'_>,
660    source: &str,
661    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
662) -> Option<FragmentedExportBody> {
663    let reparse_start = body.start_byte().checked_add(1)?;
664    if let Some(boundary) = displaced_namespace {
665        return Some(FragmentedExportBody {
666            reparse_start,
667            reparse_end: boundary.class_close.start_byte(),
668            class_range: Range {
669                start_byte: node.start_byte(),
670                end_byte: boundary.class_semicolon.end_byte(),
671                start_line: node.start_position().row + 1,
672                end_line: boundary.class_semicolon.end_position().row + 1,
673            },
674        });
675    }
676    let siblings = cpp_following_named_siblings(node, source);
677    let boundary = fragmented_export_sibling_class_boundary(node, source);
678    let boundary_index = boundary.and_then(|boundary| {
679        siblings
680            .iter()
681            .position(|candidate| same_node(*candidate, boundary))
682    });
683    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
684    let mut sibling_index = 0;
685    // A complete recovered class's synthetic wrapper is immediately followed
686    // by its displaced semicolon (comments and a trailing attribute macro --
687    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
688    // may sit between the body and that semicolon). Only scan for a later
689    // stray close when real member siblings intervene; otherwise every earlier
690    // complete class would borrow the next malformed class's close and claim
691    // its members. The trailing-attribute case is the gtest shape: the scan
692    // borrowed a close ~1900 lines later and re-owned a following
693    // `namespace testing { namespace internal {` block as class members,
694    // doubling the package path ("testing::internal::testing::internal") and
695    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
696    // boundary assert (#2297).
697    while let Some(current) = siblings.get(sibling_index).copied() {
698        if current.kind() == "comment" {
699            sibling_index += 1;
700            continue;
701        }
702        if is_trailing_attribute_macro_sibling(current) {
703            sibling_index += 1;
704            continue;
705        }
706        if cpp_is_stray_semicolon(current, source) {
707            return None;
708        }
709        break;
710    }
711    while let Some(current) = siblings.get(sibling_index).copied() {
712        let next = siblings.get(sibling_index + 1).copied();
713        if cpp_is_stray_close_brace(current, source)
714            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
715        {
716            let semicolon = next.expect("checked above");
717            return Some(FragmentedExportBody {
718                reparse_start,
719                reparse_end: current.start_byte(),
720                class_range: Range {
721                    start_byte: node.start_byte(),
722                    end_byte: semicolon.end_byte(),
723                    start_line: node.start_position().row + 1,
724                    end_line: semicolon.end_position().row + 1,
725                },
726            });
727        }
728        // When the final access label keeps the class close in its malformed
729        // declaration body, tree-sitter nests the lone `}` ERROR below the
730        // label instead of exposing it as a direct sibling. Search only the
731        // scattered siblings after the synthetic wrapper. The first such
732        // close is the class terminator because nested class bodies retain
733        // their own balanced class_specifier nodes.
734        if current.start_byte() >= body.end_byte()
735            && let Some(close) = cpp_nested_stray_close_brace(current, source)
736        {
737            return Some(FragmentedExportBody {
738                reparse_start,
739                reparse_end: close.start_byte(),
740                class_range: Range {
741                    start_byte: node.start_byte(),
742                    end_byte: current.end_byte(),
743                    start_line: node.start_position().row + 1,
744                    end_line: current.end_position().row + 1,
745                },
746            });
747        }
748        sibling_index += 1;
749    }
750    boundary.map(|boundary| FragmentedExportBody {
751        reparse_start,
752        reparse_end: boundary.start_byte(),
753        class_range: Range {
754            start_byte: node.start_byte(),
755            end_byte: boundary.start_byte(),
756            start_line: node.start_position().row + 1,
757            end_line: boundary.start_position().row + 1,
758        },
759    })
760}
761
762/// Find a later macro-export class that tree-sitter lifted through an enclosing
763/// preprocessor container. A class that is still a direct sibling can be a
764/// nested member of the current fragmented class, so only a changed parent is
765/// a proven boundary between the two recovered class envelopes.
766fn fragmented_export_sibling_class_boundary<'tree>(
767    node: Node<'tree>,
768    source: &str,
769) -> Option<Node<'tree>> {
770    let node_parent = node.parent()?;
771    cpp_following_named_siblings(node, source)
772        .into_iter()
773        .find(|candidate| {
774            recover_exported_class_function_definition(*candidate, source).is_some()
775                && candidate
776                    .parent()
777                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
778        })
779}
780
781/// A trailing attribute macro after a recovered class's closing brace, spelled
782/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
783/// bare identifier is never a class member (members need a type), so this
784/// sibling can only be the class's own tail (#2297).
785fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
786    if node.kind() != "expression_statement" {
787        return false;
788    }
789    let mut cursor = node.walk();
790    let mut children = node.named_children(&mut cursor);
791    children
792        .next()
793        .is_some_and(|child| child.kind() == "identifier")
794        && children.next().is_none()
795}
796
797/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
798/// export-class wrapper can place the class close inside an access-label node,
799/// so direct-sibling checks alone miss the boundary.  Walk named CST children
800/// only; the helper does not inspect source text beyond the existing structured
801/// stray-brace predicate.
802fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
803    let mut stack = vec![node];
804    while let Some(current) = stack.pop() {
805        if cpp_is_stray_close_brace(current, source) {
806            return Some(current);
807        }
808        let mut cursor = current.walk();
809        stack.extend(current.named_children(&mut cursor));
810    }
811    None
812}
813
814/// Return named siblings that follow `node`, including siblings that tree-sitter
815/// attached to an enclosing container after malformed recovery split the local
816/// declaration list. Stop at the first structurally visible class close so a
817/// later namespace or exported class cannot supply the recovery boundary.
818fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
819    let mut siblings = Vec::new();
820    let mut anchor = node;
821    while let Some(parent) = anchor.parent() {
822        let at_translation_unit = parent.kind() == "translation_unit";
823        let mut sibling = anchor.next_named_sibling();
824        while let Some(current) = sibling {
825            if at_translation_unit
826                && (current.kind() == "namespace_definition"
827                    || (current.kind() == "function_definition"
828                        && first_class_like_child(current).is_some()))
829            {
830                return siblings;
831            }
832            siblings.push(current);
833            if cpp_is_stray_close_brace(current, source) {
834                if let Some(semicolon) = current
835                    .next_named_sibling()
836                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
837                {
838                    siblings.push(semicolon);
839                }
840                return siblings;
841            }
842            if current.start_byte() >= node.end_byte()
843                && matches!(current.kind(), "ERROR" | "labeled_statement")
844                && cpp_nested_stray_close_brace(current, source).is_some()
845            {
846                return siblings;
847            }
848            sibling = current.next_named_sibling();
849        }
850        anchor = parent;
851    }
852    siblings
853}
854
855fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
856    if node.start_byte() >= class_end {
857        return false;
858    }
859    node.end_byte() <= class_end
860        || cpp_nested_stray_close_brace(node, source)
861            .is_some_and(|close| close.start_byte() == class_end)
862}
863
864/// Recover a plain class whose opening prefix is retained in one ERROR node
865/// while one or more nested class closes and the outer close are displaced to
866/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
867/// export-class recovery above. All boundaries come from tree-sitter nodes: the
868/// direct class tokens establish nesting depth and the displaced close nodes
869/// terminate it.
870fn fragmented_plain_class_body<'tree>(
871    node: Node<'tree>,
872    source: &str,
873) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
874    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
875        return Some(recovered);
876    }
877    let supported_container = node.kind() == "ERROR"
878        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
879    if !supported_container {
880        return None;
881    }
882    let mut cursor = node.walk();
883    let children = node.children(&mut cursor).collect::<Vec<_>>();
884    let keyword = children.first()?;
885    if !matches!(keyword.kind(), "class" | "struct" | "union") {
886        return None;
887    }
888    let name_node = children
889        .iter()
890        .copied()
891        .skip(1)
892        .find(|child| child.is_named())?;
893    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
894        return None;
895    }
896    let name = normalize_cpp_whitespace(node_text(name_node, source));
897    if name.is_empty() || cpp_export_macro_token(&name) {
898        return None;
899    }
900    let open_index = children.iter().position(|child| child.kind() == "{")?;
901    let open = children[open_index];
902    let nested_class_opens = children[open_index + 1..]
903        .iter()
904        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
905        .count();
906    let mut closes_remaining = 1 + nested_class_opens;
907    let mut sibling = node.next_named_sibling();
908    while let Some(candidate) = sibling {
909        let next = candidate.next_named_sibling();
910        if cpp_is_stray_close_brace(candidate, source) {
911            closes_remaining -= 1;
912            if closes_remaining == 0 {
913                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
914                if open.end_byte() >= candidate.start_byte() {
915                    return None;
916                }
917                return Some((
918                    node,
919                    name,
920                    FragmentedExportBody {
921                        reparse_start: open.end_byte(),
922                        reparse_end: candidate.start_byte(),
923                        class_range: Range {
924                            start_byte: node.start_byte(),
925                            end_byte: semicolon.end_byte(),
926                            start_line: node.start_position().row + 1,
927                            end_line: semicolon.end_position().row + 1,
928                        },
929                    },
930                ));
931            }
932        }
933        sibling = next;
934    }
935    None
936}
937
938pub(crate) fn recovered_fragmented_plain_class_has_body(
939    node: Node<'_>,
940    source: &str,
941    expected_name: &str,
942    expected_range: &Range,
943) -> bool {
944    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
945        name == expected_name
946            && fragmented.class_range.start_byte == expected_range.start_byte
947            && fragmented.class_range.end_byte == expected_range.end_byte
948    })
949}
950
951/// Recover a plain class whose parser-visible body ends inside a malformed
952/// inline member. Tree-sitter then attaches either the next real member
953/// declarator or the unfinished `else` branch directly to the outer function
954/// definition and leaves the class's actual `};` among later siblings. Those
955/// structured continuations and the close/semicolon siblings establish the
956/// complete body envelope without interpreting source text.
957fn fragmented_plain_class_declaration_body<'tree>(
958    node: Node<'tree>,
959    source: &str,
960) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
961    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
962        return None;
963    }
964    let class_node = node.child_by_field_name("type")?;
965    if !matches!(
966        class_node.kind(),
967        "class_specifier" | "struct_specifier" | "union_specifier"
968    ) {
969        return None;
970    }
971    let name_node = class_node.child_by_field_name("name")?;
972    let name = normalize_cpp_whitespace(node_text(name_node, source));
973    if name.is_empty() || cpp_export_macro_token(&name) {
974        return None;
975    }
976    let body = cpp_body_node(class_node)?;
977    if body.kind() != "field_declaration_list" {
978        return None;
979    }
980    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
981        if declarator.start_byte() < class_node.end_byte() {
982            return None;
983        }
984        let mut cursor = node.walk();
985        node.named_children(&mut cursor).any(|child| {
986            if child.kind() != "ERROR"
987                || child.start_byte() < class_node.end_byte()
988                || child.end_byte() > declarator.start_byte()
989            {
990                return false;
991            }
992            let mut cursor = child.walk();
993            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
994            let Some((return_type, attributes)) = components.split_last() else {
995                return false;
996            };
997            matches!(
998                return_type.kind(),
999                "identifier"
1000                    | "type_identifier"
1001                    | "primitive_type"
1002                    | "decltype"
1003                    | "placeholder_type_specifier"
1004            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1005                && attributes.iter().all(|attribute| {
1006                    matches!(attribute.kind(), "identifier" | "type_identifier")
1007                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1008                            *attribute, source,
1009                        )))
1010                })
1011        })
1012    } else {
1013        let mut cursor = node.walk();
1014        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1015        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1016            if same_node(*candidate_class, class_node)
1017                && continuation.kind() == "identifier"
1018                && node_text(*continuation, source) == "else"
1019                && continuation_body.kind() == "compound_statement"
1020                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1021                && continuation_body
1022                    .child(continuation_body.child_count().saturating_sub(1))
1023                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1024    };
1025    if !displaced_member {
1026        return None;
1027    }
1028    let open = body
1029        .children(&mut body.walk())
1030        .find(|child| child.kind() == "{")?;
1031    let siblings = cpp_following_named_siblings(node, source);
1032    let ordinary_boundary =
1033        siblings
1034            .iter()
1035            .copied()
1036            .enumerate()
1037            .find_map(|(close_index, close)| {
1038                cpp_is_stray_close_brace(close, source)
1039                    .then(|| {
1040                        siblings
1041                            .get(close_index + 1)
1042                            .copied()
1043                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1044                            .map(|semicolon| (close, semicolon))
1045                    })
1046                    .flatten()
1047            });
1048    let (close, semicolon) =
1049        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1050            (boundary.class_close, boundary.class_semicolon)
1051        } else {
1052            ordinary_boundary?
1053        };
1054    if open.end_byte() >= close.start_byte() {
1055        return None;
1056    }
1057    Some((
1058        class_node,
1059        name,
1060        FragmentedExportBody {
1061            reparse_start: open.end_byte(),
1062            reparse_end: close.start_byte(),
1063            class_range: Range {
1064                start_byte: class_node.start_byte(),
1065                end_byte: semicolon.end_byte(),
1066                start_line: class_node.start_position().row + 1,
1067                end_line: semicolon.end_position().row + 1,
1068            },
1069        },
1070    ))
1071}
1072
1073fn displaced_export_function_namespace_shape<'tree>(
1074    declaration: Node<'tree>,
1075    source: &str,
1076) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1077    let mut nested = Vec::new();
1078    for index in (0..declaration.named_child_count()).rev() {
1079        nested.push(declaration.named_child(index)?);
1080    }
1081    while let Some(current) = nested.pop() {
1082        // A recovered export class nested in this class can consume the first
1083        // parser-visible namespace close itself. In that shape the existing
1084        // later-class boundary logic already distinguishes the nested and
1085        // namespace-sibling owners; do not mistake the nested close for this
1086        // class's terminator.
1087        if recover_exported_class_function_definition(current, source).is_some() {
1088            return None;
1089        }
1090        for index in (0..current.named_child_count()).rev() {
1091            nested.push(current.named_child(index)?);
1092        }
1093    }
1094    let mut same_envelope_sibling = declaration.next_named_sibling();
1095    while let Some(current) = same_envelope_sibling {
1096        if recover_exported_class_function_definition(current, source).is_some() {
1097            return None;
1098        }
1099        same_envelope_sibling = current.next_named_sibling();
1100    }
1101    let declaration_list = declaration.parent()?;
1102    if declaration_list.kind() != "declaration_list" {
1103        return None;
1104    }
1105    let namespace = declaration_list.parent()?;
1106    if namespace.kind() != "namespace_definition"
1107        || namespace.child_by_field_name("body") != Some(declaration_list)
1108    {
1109        return None;
1110    }
1111    let class_close = direct_close_brace(declaration_list)?;
1112    let trailing_semicolon = namespace.next_named_sibling()?;
1113    if trailing_semicolon.kind() != "expression_statement"
1114        || trailing_semicolon.named_child_count() != 0
1115    {
1116        return None;
1117    }
1118    // A chain of malformed export classes can consume one parser-visible
1119    // namespace close per class. Walk through the enclosing sibling levels so
1120    // the later real namespace close remains the structural boundary; a
1121    // direct next-sibling walk stops at the first collapsed namespace and
1122    // incorrectly makes its intervening items members of this class.
1123    let siblings = cpp_following_named_siblings(namespace, source);
1124    let trailing_index = siblings
1125        .iter()
1126        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1127    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1128        recover_exported_class_function_definition(*candidate, source).is_some()
1129    }) {
1130        // Consecutive recovered classes already have an exact sibling-class
1131        // boundary. Preserve that established path, including nested export
1132        // classes, instead of interpreting the first class close as a
1133        // collapsed namespace boundary.
1134        return None;
1135    }
1136    let mut namespace_items = Vec::new();
1137    let mut nested_fragment_end = 0;
1138    for current in siblings.into_iter().skip(trailing_index + 1) {
1139        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1140        {
1141            return Some(DisplacedFragmentNamespaceBoundary {
1142                class_close,
1143                class_semicolon: trailing_semicolon,
1144                namespace_items,
1145            });
1146        }
1147        if current.start_byte() >= nested_fragment_end
1148            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1149        {
1150            nested_fragment_end = fragmented.class_range.end_byte;
1151        } else if current.start_byte() >= nested_fragment_end
1152            && recover_exported_class_function_definition(current, source).is_some()
1153            && let Some(body) = cpp_body_node(current)
1154            && let Some(fragmented) =
1155                fragmented_export_function_body_region(current, body, source, None)
1156        {
1157            nested_fragment_end = fragmented.class_range.end_byte;
1158        }
1159        namespace_items.push(current);
1160    }
1161    None
1162}
1163
1164fn displaced_fragment_namespace_boundary<'tree>(
1165    declaration: Node<'tree>,
1166    body: Node<'tree>,
1167    source: &str,
1168) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1169    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1170    let reparse_start = body.start_byte() + 1;
1171    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1172    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1173}
1174
1175/// Recover the class/namespace brace geometry for a declaration whose class
1176/// close tree-sitter consumed as the enclosing namespace close. This proof is
1177/// independent of whether every member in the class body can be reparsed: the
1178/// ordinary-tree fallback can still re-own bounded sibling declarations when
1179/// an unknown macro makes the complete body reparse unsafe.
1180fn displaced_fragment_namespace_geometry<'tree>(
1181    declaration: Node<'tree>,
1182    source: &str,
1183) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1184    // A templated class's malformed function wrapper remains beneath the
1185    // template node even though its later members have escaped to the
1186    // enclosing declaration list. Lift only that exact declaration child.
1187    let envelope = declaration
1188        .parent()
1189        .filter(|parent| {
1190            parent.kind() == "template_declaration"
1191                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1192        })
1193        .unwrap_or(declaration);
1194    let declaration_list = envelope.parent()?;
1195    if declaration_list.kind() != "declaration_list" {
1196        return None;
1197    }
1198    let namespace = declaration_list.parent()?;
1199    if namespace.kind() != "namespace_definition"
1200        || namespace.child_by_field_name("body") != Some(declaration_list)
1201    {
1202        return None;
1203    }
1204    let class_close = direct_close_brace(declaration_list)?;
1205    let trailing_semicolon = namespace.next_named_sibling()?;
1206    if trailing_semicolon.kind() != "expression_statement"
1207        || trailing_semicolon.named_child_count() != 0
1208    {
1209        return None;
1210    }
1211    let mut namespace_items = Vec::new();
1212    let mut sibling = trailing_semicolon.next_named_sibling();
1213    let mut nested_fragment_end = 0;
1214    loop {
1215        let current = sibling?;
1216        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1217        {
1218            break;
1219        }
1220        if current.start_byte() >= nested_fragment_end
1221            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1222        {
1223            nested_fragment_end = fragmented.class_range.end_byte;
1224        }
1225        namespace_items.push(current);
1226        sibling = current.next_named_sibling();
1227    }
1228    Some(DisplacedFragmentNamespaceBoundary {
1229        class_close,
1230        class_semicolon: trailing_semicolon,
1231        namespace_items,
1232    })
1233}
1234
1235/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1236fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1237    (0..node.child_count())
1238        .filter_map(|index| node.child(index))
1239        .find(|child| !child.is_named() && child.kind() == "}")
1240}
1241
1242/// A displaced lone closing brace: the class close that the fragmented multiple-base
1243/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1244fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1245    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1246}
1247
1248/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1249/// text while skipping line/block comments and string/char literals. The
1250/// exported-class recovery needs this when tree-sitter's bogus
1251/// `function_definition` body runs past the class's true closing brace and
1252/// swallows following siblings (issue #1524): the grammar tree carries no
1253/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1254/// close is located textually. Returns `None` when the text is unbalanced or
1255/// contains a construct the scanner deliberately does not interpret (raw
1256/// strings) -- callers treat that as "cannot partition" and keep the
1257/// un-split recovery.
1258fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1259    let bytes = source.as_bytes();
1260    if bytes.get(open_byte) != Some(&b'{') {
1261        return None;
1262    }
1263    let mut depth = 0usize;
1264    let mut i = open_byte;
1265    while i < bytes.len() {
1266        match bytes[i] {
1267            b'{' => depth += 1,
1268            b'}' => {
1269                depth = depth.checked_sub(1)?;
1270                if depth == 0 {
1271                    return Some(i);
1272                }
1273            }
1274            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1275                while i < bytes.len() && bytes[i] != b'\n' {
1276                    i += 1;
1277                }
1278                continue;
1279            }
1280            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1281                i += 2;
1282                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1283                    i += 1;
1284                }
1285                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1286                continue;
1287            }
1288            quote @ (b'"' | b'\'') => {
1289                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1290                // bail out rather than mis-count.
1291                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1292                    return None;
1293                }
1294                i += 1;
1295                while i < bytes.len() && bytes[i] != quote {
1296                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1297                }
1298                if i >= bytes.len() {
1299                    return None;
1300                }
1301            }
1302            _ => {}
1303        }
1304        i += 1;
1305    }
1306    None
1307}
1308
1309fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1310    let mut name = None;
1311    let mut colon_count = 0;
1312    let mut access_count = 0;
1313    for index in 0..node.child_count() {
1314        let child = node.child(index)?;
1315        match child.kind() {
1316            "identifier" | "type_identifier" if child.is_named() => {
1317                if name.is_some() {
1318                    return None;
1319                }
1320                let candidate = normalize_cpp_whitespace(node_text(child, source));
1321                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1322                    return None;
1323                }
1324                name = Some(candidate);
1325            }
1326            "template_function" | "template_type" if child.is_named() => {
1327                if name.is_some() {
1328                    return None;
1329                }
1330                let candidate = child
1331                    .child_by_field_name("name")
1332                    .and_then(|name| direct_identifier_name(name, source))?;
1333                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1334                    return None;
1335                }
1336                name = Some(candidate);
1337            }
1338            ":" if !child.is_named() => colon_count += 1,
1339            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1340            _ => return None,
1341        }
1342    }
1343    (colon_count == 1 && access_count == 1)
1344        .then_some(name)
1345        .flatten()
1346}
1347
1348fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1349    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1350        return false;
1351    }
1352    node.named_child(0)
1353        .and_then(|child| direct_identifier_name(child, source))
1354        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1355}
1356
1357fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1358    (0..node.child_count()).any(|index| {
1359        node.child(index)
1360            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1361    })
1362}
1363
1364fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1365    match node.kind() {
1366        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1367            recovered_base_atom(node, source)
1368        }
1369        "template_type" | "template_function" => node
1370            .child_by_field_name("name")
1371            .and_then(|name| recovered_malformed_base_name(name, source)),
1372        "ERROR" => None,
1373        "qualified_identifier" | "scoped_type_identifier" => {
1374            let suffix = node
1375                .child_by_field_name("name")
1376                .and_then(|name| recovered_malformed_base_name(name, source))?;
1377            let scope = node
1378                .child_by_field_name("scope")
1379                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1380            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1381                malformed_qualified_prefix(node, source)?
1382            } else {
1383                if malformed_qualified_prefix(node, source).is_some() {
1384                    return None;
1385                }
1386                scope
1387            };
1388            Some(format!("{prefix}::{suffix}"))
1389        }
1390        _ => None,
1391    }
1392}
1393
1394fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1395    if !matches!(
1396        node.kind(),
1397        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1398    ) {
1399        return None;
1400    }
1401    let name = normalize_cpp_whitespace(node_text(node, source));
1402    (!name.is_empty()).then_some(name)
1403}
1404
1405fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1406    let mut prefix = None;
1407    let mut cursor = node.walk();
1408    for error in node
1409        .named_children(&mut cursor)
1410        .filter(|child| child.kind() == "ERROR")
1411    {
1412        if error.named_child_count() != 1 || prefix.is_some() {
1413            return None;
1414        }
1415        prefix = error
1416            .named_child(0)
1417            .and_then(|child| recovered_base_atom(child, source));
1418        prefix.as_ref()?;
1419    }
1420    prefix
1421}
1422
1423/// One declaration an attribute-like macro invocation swallowed into a
1424/// declaration-scope `ERROR`, with the byte range that spells it.
1425/// What [`stranded_declaration_run`] read out of one node.
1426struct StrandedRun<'tree> {
1427    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1428    /// Whether every part of the node read as part of a declaration. False when
1429    /// a part the reader does not understand ended it early, or when the last
1430    /// parts were types with no declarator after them. Callers that index what
1431    /// was found keep the declarations either way; a caller deciding whether a
1432    /// whole region is safe to index requires this.
1433    complete: bool,
1434}
1435
1436struct MacroWrappedDeclaration<'tree> {
1437    declarator: Node<'tree>,
1438    range: Range,
1439    /// Whether the recovered declaration spells `static`. The envelope hides
1440    /// that keyword from the ordinary linkage reader, which looks for it among
1441    /// a declaration node's own children, and internal linkage is what decides
1442    /// whether a header declaration and a body in another file are one symbol.
1443    is_static: bool,
1444}
1445
1446/// Whether `node` stands where declarations live: directly in the translation
1447/// unit, or in a `namespace` or `extern "C"` body.
1448fn is_declaration_scope_position(node: Node<'_>) -> bool {
1449    let Some(parent) = node.parent() else {
1450        return false;
1451    };
1452    match parent.kind() {
1453        "translation_unit" => true,
1454        "declaration_list" => parent.parent().is_some_and(|grandparent| {
1455            matches!(
1456                grandparent.kind(),
1457                "namespace_definition" | "linkage_specification"
1458            )
1459        }),
1460        _ => false,
1461    }
1462}
1463
1464/// Whether `node` is an `ERROR` the parser produced where declarations live.
1465fn is_declaration_scope_error(node: Node<'_>) -> bool {
1466    node.kind() == "ERROR" && is_declaration_scope_position(node)
1467}
1468
1469/// Whether `node` can only be part of what precedes a declarator -- a type, a
1470/// specifier, or a word of the attribute macro's own text that the lexer left
1471/// as a bare identifier -- so a run of these followed by a declarator is one
1472/// declaration the parser failed to group.
1473fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1474    matches!(
1475        node.kind(),
1476        "identifier"
1477            | "type_identifier"
1478            | "primitive_type"
1479            | "sized_type_specifier"
1480            | "struct_specifier"
1481            | "union_specifier"
1482            | "enum_specifier"
1483            | "type_qualifier"
1484            | "storage_class_specifier"
1485            | "explicit_function_specifier"
1486            | "virtual_function_specifier"
1487            | "qualified_identifier"
1488            | "template_type"
1489            | "dependent_type"
1490            | "placeholder_type_specifier"
1491    )
1492}
1493
1494/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1495/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1496/// words the lexer reports as bare identifiers. Anything else in such an
1497/// `ERROR` stops the recovery, so a macro argument that carries structure this
1498/// recovery does not understand is never guessed at.
1499fn is_macro_argument_error(node: Node<'_>) -> bool {
1500    if node.kind() != "ERROR" {
1501        return false;
1502    }
1503    let mut cursor = node.walk();
1504    node.named_children(&mut cursor).all(|child| {
1505        matches!(
1506            child.kind(),
1507            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1508        )
1509    })
1510}
1511
1512/// The end of a recovered declaration: the declarator's end, extended across
1513/// the `;` the grammar left beside it inside the envelope.
1514///
1515/// The envelope's own end is the hard boundary. The parser hands the last
1516/// declaration's `;` to the sibling statement it recovered with, outside the
1517/// envelope, and a range that reached it would no longer lie inside one node --
1518/// which is how every reader (including the resolver's climb from a range to
1519/// the node that declares it) finds a recovered declaration again.
1520fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1521    declarator
1522        .next_sibling()
1523        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1524        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1525}
1526
1527/// The declarations `node` holds after an attribute-like macro cost the parser
1528/// their grouping, in source order.
1529///
1530/// The parser packs the parts into whichever slots it has left. In whisper's
1531/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1532/// field takes the export macro, the real return type and the *next*
1533/// declaration's declarator both end up inside one sibling `ERROR`, and the
1534/// `declarator` field takes whatever declaration the recovery reached last. In
1535/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1536/// arrive as bare identifiers and the attributed member and the member after it
1537/// share one declarator node.
1538///
1539/// Both are the same failure, so both get the same reading: flatten the node's
1540/// parts -- splicing each nested `ERROR`'s own children in place, since an
1541/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1542/// spells, a run of type-and-specifier tokens followed by a declarator, over
1543/// and over.
1544///
1545/// Fails closed. A part that is neither a declarator nor something that can
1546/// only precede one ends the recovery there, and the declarations before it are
1547/// kept.
1548fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1549    let mut parts = Vec::new();
1550    let mut cursor = node.walk();
1551    for child in node.named_children(&mut cursor) {
1552        if child.kind() == "ERROR" {
1553            let mut error_cursor = child.walk();
1554            parts.extend(child.named_children(&mut error_cursor));
1555        } else {
1556            parts.push(child);
1557        }
1558    }
1559
1560    let mut declarations = Vec::new();
1561    let mut start = None;
1562    let mut is_static = false;
1563    let mut complete = true;
1564    for part in parts {
1565        if part.kind() == "comment" {
1566            continue;
1567        }
1568        if let Some(declarator) = extract_function_declarator(part) {
1569            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1570            declarations.push(MacroWrappedDeclaration {
1571                declarator,
1572                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1573                is_static,
1574            });
1575            is_static = false;
1576            continue;
1577        }
1578        if !is_recovered_declaration_type_part(part) {
1579            complete = false;
1580            break;
1581        }
1582        is_static |= part.kind() == "storage_class_specifier"
1583            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1584        start.get_or_insert(part.start_byte());
1585    }
1586    StrandedRun {
1587        declarations,
1588        complete: complete && start.is_none(),
1589    }
1590}
1591
1592/// The declarations an attribute-like macro invocation swallowed into a
1593/// declaration-scope `ERROR`, in source order.
1594///
1595/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1596/// declaration in a macro call:
1597///
1598/// ```text
1599/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1600///                  struct llama_model * model,
1601///           struct llama_context_params   params),
1602///         "use llama_init_from_model instead");
1603/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1604/// ```
1605///
1606/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1607/// holding the macro name, the wrapped declaration as a
1608/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1609/// following declaration as a further `parameter_declaration` until it
1610/// recovers. That is what removed `llama_tokenize` from the index and left the
1611/// seven-argument call in `talk-llama.cpp` with only that file's own
1612/// three-parameter `static` overload to choose from (#2552, and the
1613/// `LLAMA_API` half of #2551).
1614///
1615/// Every part of every swallowed declaration is still a real node; only their
1616/// grouping is lost. This rebuilds the grouping and reads the nodes.
1617fn macro_wrapped_declarations<'tree>(
1618    envelope: Node<'tree>,
1619    source: &str,
1620) -> Vec<MacroWrappedDeclaration<'tree>> {
1621    let mut declarations = Vec::new();
1622    if !is_declaration_scope_error(envelope) {
1623        return declarations;
1624    }
1625    let mut cursor = envelope.walk();
1626    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1627    let [macro_name, arguments @ ..] = children.as_slice() else {
1628        return declarations;
1629    };
1630    if macro_name.kind() != "identifier" {
1631        return declarations;
1632    }
1633    let mut wrapped_declaration_seen = false;
1634    for argument in arguments {
1635        match argument.kind() {
1636            "comment" => {}
1637            "parameter_declaration" => {
1638                let recovered = stranded_declaration_run(*argument, source).declarations;
1639                if recovered.is_empty() {
1640                    break;
1641                }
1642                wrapped_declaration_seen = true;
1643                declarations.extend(recovered);
1644            }
1645            // The hint string, and only that: an argument the recovery cannot
1646            // read as a declaration is admitted before the wrapped declaration
1647            // is found, so a macro whose first argument is not a declaration
1648            // recovers nothing.
1649            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1650            _ => break,
1651        }
1652    }
1653    declarations
1654}
1655
1656/// What one macro invocation swallowed when it collapsed a whole run of
1657/// declarations into a single node.
1658struct CollapsedMacroDeclarationRun {
1659    /// The byte just past the `;` that closes the invocation, which is where
1660    /// the declarations it swallowed begin.
1661    invocation_end: usize,
1662}
1663
1664/// The macro invocation at the head of `node` when it swallowed the
1665/// declarations written after it, or `None` when `node` is not that shape.
1666///
1667/// whisper.cpp's bundled `llama.h` writes
1668///
1669/// ```text
1670/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
1671///                          const char * path_model,
1672///           struct llama_model_params   params),
1673///         "use llama_model_load_from_file instead");
1674/// ```
1675///
1676/// The wrapped declaration's own parameter list spans lines, and that alone is
1677/// enough -- no stack of such items, no `extern "C"` block -- for the parser to
1678/// read `DEPRECATED(` as a function declarator whose close it never finds. It
1679/// then consumes every declaration written after it: in the real header, 57 KB
1680/// from line 481 to line 1535, `llama_tokenize` included, which is the
1681/// `LLAMA_API` half of #2551. A `MACRO(decl, "hint");` whose wrapped
1682/// declaration fits on its line collapses nothing; the parser leaves the
1683/// declaration-scope `ERROR` that [`macro_wrapped_declarations`] reads.
1684///
1685/// The envelope is a `function_definition` when a brace block falls in the
1686/// swallowed tail -- the parser borrows it for the body the bogus definition
1687/// needs -- and an `ERROR` when none does. That says nothing about the
1688/// construct, so both are accepted. Neither is the shape
1689/// [`macro_wrapped_declarations`] reads, whose first named child is the bare
1690/// macro-name `identifier` with no declarator around it.
1691///
1692/// Fails closed. The first argument must be a declaration and the argument
1693/// after it must be the one the parser handed the invocation's own `)` and `;`,
1694/// so a macro call whose end this cannot name is left to the ordinary readers.
1695fn collapsed_macro_declaration_run(
1696    node: Node<'_>,
1697    source: &str,
1698) -> Option<CollapsedMacroDeclarationRun> {
1699    if !matches!(node.kind(), "function_definition" | "ERROR")
1700        || !is_declaration_scope_position(node)
1701    {
1702        return None;
1703    }
1704    let head = if node.kind() == "function_definition" {
1705        // A real definition names its return type here. The envelope has none:
1706        // the macro name took the declarator slot and nothing precedes it.
1707        if node.child_by_field_name("type").is_some() {
1708            return None;
1709        }
1710        node.child_by_field_name("declarator")?
1711    } else {
1712        node.named_child(0)?
1713    };
1714    let mut invocation = extract_function_declarator(head)?;
1715    if invocation.start_byte() != node.start_byte() {
1716        return None;
1717    }
1718    // Each declaration the invocation swallowed wraps another
1719    // `function_declarator` around the one before it, so the macro's own
1720    // invocation is the innermost.
1721    while let Some(inner) = invocation
1722        .child_by_field_name("declarator")
1723        .filter(|inner| inner.kind() == "function_declarator")
1724    {
1725        invocation = inner;
1726    }
1727    let name = invocation.child_by_field_name("declarator")?;
1728    if name.kind() != "identifier"
1729        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
1730    {
1731        return None;
1732    }
1733    let arguments = invocation.child_by_field_name("parameters")?;
1734    let mut cursor = arguments.walk();
1735    let mut children = arguments
1736        .children(&mut cursor)
1737        .filter(|child| child.kind() != "comment");
1738    if children.next()?.kind() != "(" || children.next()?.kind() != "parameter_declaration" {
1739        return None;
1740    }
1741    // The argument after the wrapped declaration is where the parser put the
1742    // invocation's own `)` and `;`. Their adjacency inside that argument is
1743    // where the invocation ends; whatever follows them there, or after the
1744    // argument, is what the invocation swallowed. A `)` the lexer left inside
1745    // the hint text is not followed by a `;`, so the pair names the end and
1746    // nothing else does.
1747    let hint = children.find(|child| child.kind() != ",")?;
1748    if hint.kind() != "ERROR" {
1749        return None;
1750    }
1751    let mut hint_cursor = hint.walk();
1752    let parts = hint.children(&mut hint_cursor).collect::<Vec<_>>();
1753    let invocation_end = parts.windows(2).find_map(|pair| {
1754        let [close, semicolon] = pair else {
1755            return None;
1756        };
1757        (close.kind() == ")"
1758            && !close.is_missing()
1759            && semicolon.kind() == ";"
1760            && !semicolon.is_missing())
1761        .then(|| semicolon.end_byte())
1762    })?;
1763    // An invocation that ends where the node does swallowed nothing after it.
1764    (invocation_end < node.end_byte()).then_some(CollapsedMacroDeclarationRun { invocation_end })
1765}
1766
1767/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
1768/// ordinary class body, and the members it swallowed.
1769///
1770/// Botan deprecates members that way:
1771///
1772/// ```text
1773/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
1774/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
1775/// ```
1776///
1777/// The parser reads the macro name as the member's type and its argument list
1778/// as a parenthesized declarator, which then swallows the attributed member
1779/// *and* the member written after it: a `field_declaration` whose `type` is a
1780/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
1781/// opening with an `ERROR` whose first part is a bare identifier -- the first
1782/// word of the string, which the lexer could not keep together.
1783///
1784/// The macro's spelling is not the criterion; that structural shape is. The
1785/// returned declarations are in source order, the first being the attributed
1786/// member and the rest the members the declarator swallowed after it.
1787fn string_attribute_macro_member_declarators<'tree>(
1788    field: Node<'tree>,
1789    source: &str,
1790) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
1791    if field.kind() != "field_declaration"
1792        || field
1793            .child_by_field_name("type")
1794            .is_none_or(|type_node| type_node.kind() != "type_identifier")
1795    {
1796        return None;
1797    }
1798    let declarator = field.child_by_field_name("declarator")?;
1799    if declarator.kind() != "parenthesized_declarator" {
1800        return None;
1801    }
1802    let opening = declarator.named_child(0)?;
1803    if opening.kind() != "ERROR"
1804        || opening
1805            .named_child(0)
1806            .is_none_or(|word| word.kind() != "identifier")
1807    {
1808        return None;
1809    }
1810    let declarations = stranded_declaration_run(declarator, source).declarations;
1811    (!declarations.is_empty()).then_some(declarations)
1812}
1813
1814/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
1815/// export-macro class body leaves in front of the members that macro decorated.
1816///
1817/// The reparse reads the class body as statements, so the attribute becomes a
1818/// call statement of its own -- with the `;` the grammar had to invent -- and
1819/// the members after it are stranded in the `ERROR` that follows.
1820fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
1821    let Some(call) = (node.kind() == "expression_statement")
1822        .then(|| node.named_child(0))
1823        .flatten()
1824        .filter(|child| child.kind() == "call_expression")
1825    else {
1826        return false;
1827    };
1828    call.child_by_field_name("function")
1829        .is_some_and(|function| function.kind() == "identifier")
1830        && call
1831            .child_by_field_name("arguments")
1832            .is_some_and(|arguments| {
1833                let mut cursor = arguments.walk();
1834                arguments.named_child_count() > 0
1835                    && arguments
1836                        .named_children(&mut cursor)
1837                        .all(|argument| argument.kind() == "string_literal")
1838            })
1839}
1840
1841/// The start byte of the call the region reparse left where an access-labeled
1842/// constructor was written.
1843///
1844/// A constructor is a member only inside a class body. The export-macro class
1845/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
1846/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
1847/// comma-expression under `private:`, with no declarator left in the tree. It
1848/// is still spelled in the source, though, starting at the one call under the
1849/// label whose callee is the class's own name. Botan's private six-parameter
1850/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
1851/// call is an ambiguity this declines, and so is a reparse from that byte that
1852/// does not yield exactly one constructor declarator there.
1853fn cpp_access_label_constructor_call_start(
1854    node: Node<'_>,
1855    class_name: &str,
1856    source: &str,
1857) -> Option<usize> {
1858    if node.kind() != "labeled_statement" {
1859        return None;
1860    }
1861    let label = node.named_child(0)?;
1862    if label.kind() != "statement_identifier"
1863        || !matches!(
1864            node_text(label, source).trim(),
1865            "public" | "private" | "protected"
1866        )
1867    {
1868        return None;
1869    }
1870    let mut starts = Vec::new();
1871    let mut stack = vec![node];
1872    while let Some(current) = stack.pop() {
1873        if current.kind() == "call_expression"
1874            && current
1875                .child_by_field_name("function")
1876                .is_some_and(|function| {
1877                    function.kind() == "identifier"
1878                        && node_text(function, source).trim() == class_name
1879                })
1880        {
1881            starts.push(current.start_byte());
1882        }
1883        let mut cursor = current.walk();
1884        stack.extend(current.named_children(&mut cursor));
1885    }
1886    let [start] = starts.as_slice() else {
1887        return None;
1888    };
1889    Some(*start)
1890}
1891
1892/// The `function_definition` that gives `declarator` a body, following only the
1893/// declarator chain, so a recovered callable knows whether it is a declaration
1894/// or a definition without anyone having to say.
1895fn cpp_declarator_function_definition<'tree>(
1896    declarator: Node<'tree>,
1897    ancestry: &ParentIndex<'tree>,
1898) -> Option<Node<'tree>> {
1899    let mut current = declarator;
1900    while let Some(parent) = ancestry.parent(current) {
1901        match parent.kind() {
1902            "function_definition" if parent.child_by_field_name("body").is_some() => {
1903                return Some(parent);
1904            }
1905            "pointer_declarator"
1906            | "reference_declarator"
1907            | "parenthesized_declarator"
1908            | "array_declarator" => current = parent,
1909            _ => return None,
1910        }
1911    }
1912    None
1913}
1914
1915/// Whether `node` lies in the body of a `namespace_definition` the ordinary
1916/// declaration walk still reaches, so a recovery that runs ahead of that walk
1917/// would name what it finds without the namespace.
1918fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1919    let mut current = node;
1920    while let Some(parent) = ancestry.parent(current) {
1921        if parent.kind() == "namespace_definition"
1922            && parent.child_by_field_name("body") == Some(current)
1923        {
1924            return true;
1925        }
1926        current = parent;
1927    }
1928    false
1929}
1930
1931/// Whether the source a recovered callable's byte range spells is a definition
1932/// (a declarator with a body) rather than a declaration.
1933///
1934/// A callable recovered from a mangled region owns no node of its own in the
1935/// file's tree, so the occurrence-role scan that climbs from the range to the
1936/// node containing it lands on whatever container the parser left and answers
1937/// for that instead -- which called Botan's inline `XMSS_Parameters` private
1938/// constructor a declaration and left its call site with nothing to navigate to
1939/// (#2552). Reparse the range on its own, the same offset-preserving reparse
1940/// extraction used, and read the answer from the one declaration it spells.
1941///
1942/// `None` when the range is not one recovered declaration on its own, which is
1943/// the case for every ordinary declaration, so callers keep their own answer.
1944pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
1945    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
1946    let root = tree.root_node();
1947    let mut cursor = root.walk();
1948    let items = root
1949        .named_children(&mut cursor)
1950        .filter(|child| child.kind() != "comment")
1951        .collect::<Vec<_>>();
1952    let [item] = items.as_slice() else {
1953        return None;
1954    };
1955    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
1956        return None;
1957    }
1958    match item.kind() {
1959        "function_definition" => Some(item.child_by_field_name("body").is_some()),
1960        "declaration" | "field_declaration" => Some(false),
1961        _ => None,
1962    }
1963}
1964
1965/// Whether `node` is an envelope an attribute-like macro invocation left where
1966/// declarations were written: the declaration-scope `ERROR`
1967/// [`macro_wrapped_declarations`] reads, or the collapsed run
1968/// [`collapsed_macro_declaration_run`] reads.
1969///
1970/// Extraction and resolution must read one definition of this shape. The
1971/// resolver climbs from a declaration's recorded byte range to the node that
1972/// declares it, and a recovered declaration's range lies inside one of these
1973/// envelopes rather than inside a `declaration` node, so the climb stops here
1974/// (the same role `is_recovered_exported_class_container` plays for a recovered
1975/// class).
1976pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
1977    !macro_wrapped_declarations(node, source).is_empty()
1978        || collapsed_macro_declaration_run(node, source).is_some()
1979}
1980
1981fn recover_exported_class_function_definition<'tree>(
1982    node: Node<'tree>,
1983    source: &str,
1984) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1985    if node.kind() != "function_definition" {
1986        return None;
1987    }
1988    if let Some(prefix) = node.prev_named_sibling()
1989        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
1990        && recovered.range.end_byte == node.end_byte()
1991    {
1992        return Some((node, recovered.name, recovered.raw_supertypes));
1993    }
1994    let type_node = node.child_by_field_name("type")?;
1995    let declarator = node.child_by_field_name("declarator")?;
1996
1997    if matches!(
1998        type_node.kind(),
1999        "class_specifier" | "struct_specifier" | "union_specifier"
2000    ) {
2001        let type_name = type_node
2002            .child_by_field_name("name")
2003            .and_then(|name| direct_identifier_name(name, source));
2004        let exported_macro_type = type_name
2005            .as_ref()
2006            .is_some_and(|name| cpp_export_macro_token(name));
2007        if exported_macro_type {
2008            let mut cursor = node.walk();
2009            let errors_before_declarator = node
2010                .named_children(&mut cursor)
2011                .filter(|child| {
2012                    child.kind() == "ERROR"
2013                        && child.start_byte() >= type_node.end_byte()
2014                        && child.end_byte() <= declarator.start_byte()
2015                })
2016                .collect::<Vec<_>>();
2017            if let Some(name) = errors_before_declarator
2018                .iter()
2019                .find_map(|error| displaced_exported_class_name(*error, source))
2020            {
2021                let raw_supertypes = errors_before_declarator
2022                    .iter()
2023                    .any(|error| malformed_inheritance_syntax(*error))
2024                    .then(|| recovered_malformed_base_name(declarator, source))
2025                    .flatten()
2026                    .map(|base| vec![base]);
2027                return Some((node, name, raw_supertypes));
2028            }
2029            if errors_before_declarator
2030                .iter()
2031                .any(|error| malformed_inheritance_syntax(*error))
2032            {
2033                return None;
2034            }
2035        }
2036        if !exported_macro_type
2037            && let Some(name) = type_name
2038            && !cpp_export_macro_token(&name)
2039            && let Some(base) =
2040                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2041        {
2042            return Some((node, name, Some(vec![base])));
2043        }
2044        if let Some(name) = direct_identifier_name(declarator, source)
2045            && exported_macro_type
2046            && !cpp_export_macro_token(&name)
2047        {
2048            let raw_supertypes = exported_macro_type
2049                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2050                .flatten()
2051                .map(|base| vec![base]);
2052            return Some((node, name, raw_supertypes));
2053        }
2054        if declarator.kind() == "parenthesized_declarator"
2055            && type_node
2056                .child_by_field_name("name")
2057                .and_then(|name| direct_identifier_name(name, source))
2058                .is_some_and(|name| cpp_export_macro_token(&name))
2059        {
2060            if let Some((name, base)) =
2061                recovered_function_like_export_class_owner(declarator, source)
2062            {
2063                return Some((node, name, Some(vec![base])));
2064            }
2065            let body_start = node
2066                .child_by_field_name("body")
2067                .map(|body| body.start_byte())
2068                .unwrap_or(node.end_byte());
2069            let mut cursor = node.walk();
2070            if let Some(name) = node
2071                .named_children(&mut cursor)
2072                .filter(|child| {
2073                    child.kind() == "ERROR"
2074                        && child.start_byte() >= declarator.end_byte()
2075                        && child.end_byte() <= body_start
2076                })
2077                .find_map(|error| declarator_name_from_node(error, source))
2078            {
2079                return Some((node, name, None));
2080            }
2081        }
2082    }
2083
2084    let declarator_text = direct_identifier_name(declarator, source)?;
2085    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2086        return None;
2087    }
2088    class_identifier_before_body(node, source).map(|name| (node, name, None))
2089}
2090
2091fn recovered_function_like_export_class_owner(
2092    declarator: Node<'_>,
2093    source: &str,
2094) -> Option<(String, String)> {
2095    if declarator.kind() != "parenthesized_declarator" {
2096        return None;
2097    }
2098    let mut cursor = declarator.walk();
2099    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2100    let [prefix, base] = children.as_slice() else {
2101        return None;
2102    };
2103    if prefix.kind() != "ERROR"
2104        || !matches!(
2105            base.kind(),
2106            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2107        )
2108    {
2109        return None;
2110    }
2111    let mut identifiers = Vec::new();
2112    let mut prefix_cursor = prefix.walk();
2113    for child in prefix.named_children(&mut prefix_cursor) {
2114        match child.kind() {
2115            "number_literal" | "string_literal" | "char_literal" => {}
2116            "identifier" | "type_identifier" => {
2117                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2118            }
2119            _ => return None,
2120        }
2121    }
2122    let name = match identifiers.as_slice() {
2123        [name] => name.clone(),
2124        [name, final_token] if final_token == "final" => name.clone(),
2125        _ => return None,
2126    };
2127    if name.is_empty() || cpp_export_macro_token(&name) {
2128        return None;
2129    }
2130    let base = recovered_malformed_base_name(*base, source)?;
2131    Some((name, base))
2132}
2133
2134/// Collect the base names tree-sitter scattered across the recovered head of a
2135/// function-like export-macro class. `skip` names the structural children that
2136/// are not bases, such as the class name and the body. A base arrives either as
2137/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2138/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2139/// the access specifiers in the same position as the bases, so drop them.
2140/// The bases a recovered function-like export-macro class head names between
2141/// `after` (the end of the class name, or of the access specifier that stands
2142/// in for it) and `before` (the start of the body, or of the `init_declarator`
2143/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2144/// fragments, and one fragment can also hold the class name and `final`
2145/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2146/// than by the fragment that holds it.
2147fn recovered_export_head_bases(
2148    node: Node<'_>,
2149    after: usize,
2150    before: usize,
2151    source: &str,
2152) -> Vec<String> {
2153    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2154    let mut bases = Vec::new();
2155    let mut cursor = node.walk();
2156    for child in node.named_children(&mut cursor) {
2157        if child.kind() == "ERROR" {
2158            let mut error_cursor = child.walk();
2159            bases.extend(
2160                child
2161                    .named_children(&mut error_cursor)
2162                    .filter(within)
2163                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2164            );
2165        } else if within(&child)
2166            && let Some(base) = recovered_malformed_base_name(child, source)
2167        {
2168            bases.push(base);
2169        }
2170    }
2171    bases.retain(|base| !matches!(base.as_str(), "final" | "public" | "protected" | "private"));
2172    bases
2173}
2174
2175/// Whether `token` is the `final` that closes a recovered class head. The
2176/// grammar keeps it as the `final` keyword when it recognised the head
2177/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2178/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2179fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2180    if token.is_named() {
2181        token.kind() == "identifier" && node_text(token, source) == "final"
2182    } else {
2183        token.kind() == "final"
2184    }
2185}
2186
2187/// The class name of a recovered function-like export-macro class head, read by
2188/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2189/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2190/// carries the body. The head's tokens, in source order with `ERROR` fragments
2191/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2192/// identifier before the head ends, and the head ends at `final`, at the base
2193/// clause `:`, or at `tail`. Every identifier before the name is decoration
2194/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2195/// (`X509_CA`) is a class name like any other.
2196///
2197/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2198/// rule, not a token of the source, so it is never the name.
2199fn recovered_export_head_name<'tree>(
2200    node: Node<'tree>,
2201    tail: Node<'tree>,
2202    source: &str,
2203) -> Option<Node<'tree>> {
2204    let mut tokens = Vec::new();
2205    let mut cursor = node.walk();
2206    for child in node.children(&mut cursor) {
2207        if child.start_byte() >= tail.start_byte() {
2208            break;
2209        }
2210        if child.kind() == "ERROR" {
2211            let mut fragment_cursor = child.walk();
2212            tokens.extend(child.children(&mut fragment_cursor));
2213        } else {
2214            tokens.push(child);
2215        }
2216    }
2217    let mut name = None;
2218    for token in tokens {
2219        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2220        {
2221            break;
2222        }
2223        if token.is_named()
2224            && !token.is_missing()
2225            && matches!(
2226                token.kind(),
2227                "identifier" | "type_identifier" | "field_identifier"
2228            )
2229        {
2230            name = Some(token);
2231        }
2232    }
2233    name
2234}
2235
2236/// The `init_declarator` whose `value` carries the class body when the grammar
2237/// keeps a function-like export-macro class head as one `declaration`.
2238fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2239    let mut cursor = declaration.walk();
2240    declaration
2241        .named_children(&mut cursor)
2242        .find(|child| child.kind() == "init_declarator")
2243}
2244
2245/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2246/// of a function-like export-macro class head. The grammar splits a base list
2247/// across bare declarator fields, `ERROR` fragments, and one trailing
2248/// `init_declarator` whose `value` is the class body. `head_end` is where the
2249/// class identity ends and the bases can begin: the end of the access specifier
2250/// when the class name went to a statement label, and the end of the class name
2251/// itself otherwise.
2252fn recovered_export_declaration_tail<'tree>(
2253    declaration: Node<'tree>,
2254    head_end: usize,
2255    source: &str,
2256) -> Option<(Vec<String>, Node<'tree>)> {
2257    let init = recovered_export_init_declarator(declaration)?;
2258    let body = init.child_by_field_name("value")?;
2259    if body.kind() != "initializer_list" {
2260        return None;
2261    }
2262    let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2263    bases.extend(recovered_export_head_bases(
2264        init,
2265        init.start_byte(),
2266        body.start_byte(),
2267        source,
2268    ));
2269    Some((bases, body))
2270}
2271
2272fn recover_function_like_export_class_pair(
2273    node: Node<'_>,
2274    source: &str,
2275) -> Option<RecoveredFunctionLikeExportClassPair> {
2276    if node.kind() != "ERROR" {
2277        return None;
2278    }
2279    let class_node = first_class_like_child(node)?;
2280    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2281        return None;
2282    }
2283    // The identifier tree-sitter took for the class name is the export macro,
2284    // a function-like invocation when the `(` of its argument list follows it
2285    // directly inside the error. Spelling plays no part (#2557).
2286    class_node
2287        .child_by_field_name("name")
2288        .and_then(|name| direct_identifier_name(name, source))?;
2289    let invocation = class_node.next_sibling()?;
2290    if invocation.is_named() || invocation.kind() != "(" {
2291        return None;
2292    }
2293    let sibling = node.next_named_sibling()?;
2294    let (name, raw_supertypes, body) = match sibling.kind() {
2295        // At translation-unit scope tree-sitter can leave the malformed class
2296        // head in one ERROR node and parse its body as the adjacent compound
2297        // statement. The head still carries the export invocation, displaced
2298        // class name, and optional `final` token in source order, so recover
2299        // the name by the same positional rule as the other shapes.
2300        "compound_statement" => (
2301            recovered_export_head_name(node, sibling, source)
2302                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2303            None,
2304            sibling,
2305        ),
2306        "expression_statement" => {
2307            let compound = sibling.named_child(0)?;
2308            if compound.kind() != "compound_literal_expression" {
2309                return None;
2310            }
2311            let body = compound.child_by_field_name("value")?;
2312            if body.kind() != "initializer_list" {
2313                return None;
2314            }
2315            (
2316                compound
2317                    .child_by_field_name("type")
2318                    .and_then(|name| direct_identifier_name(name, source))?,
2319                None,
2320                body,
2321            )
2322        }
2323        "labeled_statement" => {
2324            let label = sibling.child_by_field_name("label")?;
2325            if label.kind() != "statement_identifier" {
2326                return None;
2327            }
2328            let name = normalize_cpp_whitespace(node_text(label, source));
2329            let declaration = sibling
2330                .named_children(&mut sibling.walk())
2331                .find(|child| child.kind() == "declaration")?;
2332            let access = declaration.child_by_field_name("type")?;
2333            if !matches!(
2334                node_text(access, source),
2335                "public" | "protected" | "private"
2336            ) {
2337                return None;
2338            }
2339            let (bases, body) =
2340                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2341            (name, (!bases.is_empty()).then_some(bases), body)
2342        }
2343        // `class MACRO(2, 0) Name final { ... };`,
2344        // `class MACRO(2, 0) Name final : public Base { ... };`, and
2345        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
2346        // spread over the `type` field, the declarator field, and `ERROR`
2347        // fragments beside them; the name is the last one before the head ends.
2348        "function_definition" => {
2349            let body = sibling.child_by_field_name("body")?;
2350            if body.kind() != "compound_statement" {
2351                return None;
2352            }
2353            let name_node = recovered_export_head_name(sibling, body, source)?;
2354            let bases = recovered_export_head_bases(
2355                sibling,
2356                name_node.end_byte(),
2357                body.start_byte(),
2358                source,
2359            );
2360            (
2361                normalize_cpp_whitespace(node_text(name_node, source)),
2362                (!bases.is_empty()).then_some(bases),
2363                body,
2364            )
2365        }
2366        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2367        // comma-separated base list makes the grammar keep the whole tail as one
2368        // declaration whose trailing `init_declarator` carries the body.
2369        "declaration" => {
2370            let init = recovered_export_init_declarator(sibling)?;
2371            let name_node = recovered_export_head_name(sibling, init, source)?;
2372            let (bases, body) =
2373                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2374            (
2375                normalize_cpp_whitespace(node_text(name_node, source)),
2376                (!bases.is_empty()).then_some(bases),
2377                body,
2378            )
2379        }
2380        _ => return None,
2381    };
2382    debug_assert!(
2383        !name.is_empty(),
2384        "a recovered class head names its class by an identifier token"
2385    );
2386    let range = Range {
2387        start_byte: node.start_byte(),
2388        end_byte: sibling.end_byte(),
2389        start_line: node.start_position().row + 1,
2390        end_line: sibling.end_position().row + 1,
2391    };
2392    Some(RecoveredFunctionLikeExportClassPair {
2393        name,
2394        raw_supertypes,
2395        range,
2396        fragmented_body: recovered_fragmented_export_body(body, range)?,
2397    })
2398}
2399
2400/// Recover a function-like export-macro class that tree-sitter embedded in a
2401/// larger error after an earlier malformed class body. The grammar still
2402/// preserves every part of the class head: the `class` token, export macro
2403/// identifier and argument list, displaced class identifier, access specifier,
2404/// base field, and initializer-list-shaped body. Match only that complete
2405/// structured sequence and keep each recovered class's exact byte envelope.
2406fn recover_embedded_function_like_export_classes(
2407    node: Node<'_>,
2408    source: &str,
2409) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2410    if node.kind() != "ERROR" {
2411        return Vec::new();
2412    }
2413
2414    let mut nodes = Vec::new();
2415    let mut stack = vec![node];
2416    while let Some(current) = stack.pop() {
2417        nodes.push(current);
2418        for index in (0..current.child_count()).rev() {
2419            stack.push(
2420                current
2421                    .child(index)
2422                    .expect("index below the node's own child count"),
2423            );
2424        }
2425    }
2426    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2427
2428    let mut recovered = Vec::new();
2429    for class_token in nodes
2430        .iter()
2431        .copied()
2432        .filter(|child| !child.is_named() && child.kind() == "class")
2433    {
2434        let row = class_token.start_position().row;
2435        // The head after the `class` token reads `MACRO(args) macros... name
2436        // [final] : bases`. The macro is the first identifier, a function-like
2437        // invocation when its argument list is the next thing after it, and the
2438        // name is the last identifier before the head ends at `final` or at the
2439        // base clause `:`. Spelling plays no part (#2557).
2440        let is_identifier = |candidate: &Node<'_>| {
2441            !candidate.is_missing()
2442                && matches!(
2443                    candidate.kind(),
2444                    "identifier" | "type_identifier" | "field_identifier"
2445                )
2446        };
2447        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2448            candidate.start_byte() >= class_token.end_byte()
2449                && candidate.start_position().row == row
2450                && is_identifier(candidate)
2451        }) else {
2452            continue;
2453        };
2454        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2455            candidate.kind() == "argument_list"
2456                && candidate.start_byte() >= macro_name.end_byte()
2457                && candidate.start_position().row == row
2458        }) else {
2459            continue;
2460        };
2461        if nodes.iter().any(|candidate| {
2462            is_identifier(candidate)
2463                && candidate.start_byte() >= macro_name.end_byte()
2464                && candidate.end_byte() <= arguments.start_byte()
2465        }) {
2466            continue;
2467        }
2468        let Some(head_end) = nodes.iter().copied().find(|candidate| {
2469            candidate.start_byte() >= arguments.end_byte()
2470                && (recovered_export_head_final(*candidate, source)
2471                    || (!candidate.is_named() && candidate.kind() == ":"))
2472        }) else {
2473            continue;
2474        };
2475        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2476            is_identifier(candidate)
2477                && candidate.start_byte() >= arguments.end_byte()
2478                && candidate.end_byte() <= head_end.start_byte()
2479                && candidate.start_position().row == row
2480        }) else {
2481            continue;
2482        };
2483        let name = normalize_cpp_whitespace(node_text(name_node, source));
2484        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2485            candidate.kind() == "field_initializer"
2486                && candidate.start_byte() >= name_node.end_byte()
2487                && candidate
2488                    .child_by_field_name("field")
2489                    .or_else(|| candidate.named_child(0))
2490                    .is_some()
2491                && candidate
2492                    .child_by_field_name("value")
2493                    .or_else(|| {
2494                        let mut cursor = candidate.walk();
2495                        candidate
2496                            .named_children(&mut cursor)
2497                            .find(|child| child.kind() == "initializer_list")
2498                    })
2499                    .is_some_and(|value| value.kind() == "initializer_list")
2500        }) else {
2501            continue;
2502        };
2503        let has_access = nodes.iter().copied().any(|candidate| {
2504            candidate.start_byte() >= name_node.end_byte()
2505                && candidate.end_byte() <= base_initializer.start_byte()
2506                && matches!(
2507                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2508                    "public" | "protected" | "private"
2509                )
2510        });
2511        if !has_access {
2512            continue;
2513        }
2514        let Some(base_node) = base_initializer
2515            .child_by_field_name("field")
2516            .or_else(|| base_initializer.named_child(0))
2517        else {
2518            continue;
2519        };
2520        let Some(base) = recovered_malformed_base_name(base_node, source) else {
2521            continue;
2522        };
2523        let body = base_initializer
2524            .child_by_field_name("value")
2525            .or_else(|| {
2526                let mut cursor = base_initializer.walk();
2527                base_initializer
2528                    .named_children(&mut cursor)
2529                    .find(|child| child.kind() == "initializer_list")
2530            })
2531            .expect("initializer-list value checked above");
2532        let range = Range {
2533            start_byte: class_token.start_byte(),
2534            end_byte: body.end_byte(),
2535            start_line: class_token.start_position().row + 1,
2536            end_line: body.end_position().row + 1,
2537        };
2538        if recovered
2539            .iter()
2540            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2541                existing.name == name && existing.range == range
2542            })
2543        {
2544            continue;
2545        }
2546        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2547            name,
2548            range,
2549            raw_supertypes: vec![base],
2550            fragmented_body: match recovered_fragmented_export_body(body, range) {
2551                Some(fragmented) => fragmented,
2552                None => continue,
2553            },
2554        });
2555    }
2556    recovered
2557}
2558
2559fn lifted_function_like_export_class_namespace<'tree>(
2560    node: Node<'tree>,
2561    source: &str,
2562    ancestry: &ParentIndex<'tree>,
2563) -> Option<String> {
2564    // A long malformed body can embed the next exported class several levels
2565    // below a bogus top-level function_definition. Compare namespace evidence
2566    // against that top-level envelope, not only the recovered ERROR's direct
2567    // parent. The source tree still proves the same boundary: one earlier
2568    // malformed namespace and one later standalone closing brace.
2569    let mut anchor = node;
2570    let parent = loop {
2571        let parent = ancestry.parent(anchor)?;
2572        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2573            break parent;
2574        }
2575        anchor = parent;
2576    };
2577    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2578        sibling.start_byte() > anchor.end_byte()
2579            && sibling.kind() == "ERROR"
2580            && sibling.named_child_count() == 0
2581            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2582    });
2583    if !has_later_close {
2584        return None;
2585    }
2586    let candidates = parent
2587        .named_children(&mut parent.walk())
2588        .filter(|sibling| {
2589            sibling.kind() == "namespace_definition"
2590                && sibling.has_error()
2591                && sibling.end_byte() < anchor.start_byte()
2592        })
2593        .filter_map(|namespace| {
2594            namespace
2595                .child_by_field_name("name")
2596                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2597                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2598        })
2599        .collect::<Vec<_>>();
2600    let [namespace] = candidates.as_slice() else {
2601        return None;
2602    };
2603    Some(namespace.clone())
2604}
2605
2606pub(crate) fn recovered_function_like_export_class_pair_has_body(
2607    node: Node<'_>,
2608    source: &str,
2609    identifier: &str,
2610    range: &Range,
2611) -> bool {
2612    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2613        recovered.name == identifier
2614            && recovered.range.start_byte == range.start_byte
2615            && recovered.range.end_byte == range.end_byte
2616    })
2617}
2618
2619/// One file's embedded export-macro class recovery, resolved once and keyed by
2620/// the `ERROR` node that mints each set.
2621///
2622/// [`recover_embedded_function_like_export_classes`] collects and sorts an
2623/// `ERROR` node's whole subtree, and the declaration-strength question asks it
2624/// once per class-like unit in the file. On a translation unit the parser could
2625/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
2626/// node spans most of the file -- that is one full subtree pass per class,
2627/// quadratic in the file's size, and it was 78% of that file's inverse scan
2628/// (#1496).
2629///
2630/// Keyed by byte span rather than node identity, so one analyzer generation's
2631/// re-parses of the same content share the index. A nested `ERROR` that shares
2632/// its parent's span recovers the same classes: the only node the parent adds
2633/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
2634#[derive(Default)]
2635pub struct CppRecoveredExportClassIndex {
2636    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2637}
2638
2639impl CppRecoveredExportClassIndex {
2640    pub fn build(root: Node<'_>, source: &str) -> Self {
2641        let mut by_error_node: HashMap<
2642            (usize, usize),
2643            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2644        > = HashMap::default();
2645        let mut stack = vec![root];
2646        while let Some(node) = stack.pop() {
2647            if node.kind() == "ERROR" {
2648                let recovered = recover_embedded_function_like_export_classes(node, source);
2649                if !recovered.is_empty() {
2650                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
2651                }
2652            }
2653            let mut cursor = node.walk();
2654            stack.extend(node.named_children(&mut cursor));
2655        }
2656        Self { by_error_node }
2657    }
2658
2659    /// The bytes this index holds, for the analyzer cache's weight.
2660    pub fn approximate_size(&self) -> usize {
2661        self.by_error_node
2662            .values()
2663            .fold(0usize, |total, recovered| {
2664                recovered.iter().fold(
2665                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
2666                    |acc, class| {
2667                        acc.saturating_add(std::mem::size_of::<
2668                            RecoveredEmbeddedFunctionLikeExportClass,
2669                        >())
2670                        .saturating_add(class.name.len())
2671                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
2672                    },
2673                )
2674            })
2675    }
2676
2677    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
2678        self.by_error_node
2679            .get(&(node.start_byte(), node.end_byte()))
2680            .is_some_and(|recovered| {
2681                recovered.iter().any(|class| {
2682                    class.name == identifier
2683                        && class.range.start_byte == range.start_byte
2684                        && class.range.end_byte == range.end_byte
2685                })
2686            })
2687    }
2688}
2689
2690// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
2691// `recovered_class_body_at` pops while deciding whether a recovered class shape
2692// claims one declaration range. The count is deterministic for a given source,
2693// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
2694// pins it directly instead of timing the walk, the way #2358 pinned the
2695// `remove_code_unit` scan.
2696#[cfg(any(test, feature = "test-support"))]
2697thread_local! {
2698    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
2699        const { std::cell::Cell::new(0) };
2700}
2701
2702/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
2703/// the calling thread since the last
2704/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
2705#[cfg(any(test, feature = "test-support"))]
2706#[doc(hidden)]
2707pub fn recovered_class_body_node_visits_for_test() -> usize {
2708    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
2709}
2710
2711/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
2712#[cfg(any(test, feature = "test-support"))]
2713#[doc(hidden)]
2714pub fn reset_recovered_class_body_node_visits_for_test() {
2715    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
2716}
2717
2718#[cfg(any(test, feature = "test-support"))]
2719fn record_recovered_class_body_visit() {
2720    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
2721}
2722
2723#[cfg(not(any(test, feature = "test-support")))]
2724fn record_recovered_class_body_visit() {}
2725
2726/// Whether a recovered class shape named `identifier` owns `range`, and if so
2727/// whether that shape has a body.
2728///
2729/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
2730/// forward declaration, and `None` means no recovered shape claims the range,
2731/// so the caller reads the plain `class_specifier` family instead. This is the
2732/// single definition of "does a recovered class have a body": the resolver's
2733/// declaration-strength answer and the navigation occurrence role both read it,
2734/// so an export-macro class is a definition on both paths.
2735///
2736/// The walk follows only the nodes whose span covers `range.start_byte`, which
2737/// is every node that can answer. Each recovered shape reports a range that
2738/// starts at the node's own start byte (the function-like export pair, whose
2739/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
2740/// class, which the caller gates on an equal start) or at a token inside the
2741/// node (the embedded export class, keyed on its `class` token, and the
2742/// exported class wrapper, gated on containment here), and every one of them is
2743/// accepted only on an exact match with `range`. Descending everywhere instead
2744/// made one declaration-strength question cost a full pass over the file, so
2745/// asking it once per reference was quadratic in file size: 80% of the 385 s
2746/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
2747/// (#1496).
2748pub(crate) fn recovered_class_body_at(
2749    recovered_export_classes: &CppRecoveredExportClassIndex,
2750    root: Node<'_>,
2751    source: &str,
2752    identifier: &str,
2753    range: &Range,
2754) -> Option<bool> {
2755    let covers_range_start = |node: &Node<'_>| {
2756        node.start_byte() <= range.start_byte
2757            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
2758    };
2759    let mut stack = vec![root];
2760    let mut saw_forward = false;
2761    while let Some(node) = stack.pop() {
2762        record_recovered_class_body_visit();
2763        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
2764        // so an unequal start settles it before the recovery reads the node's
2765        // children at all.
2766        if (node.start_byte() == range.start_byte
2767            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
2768            || recovered_export_classes.claims(node, identifier, range)
2769            || (node.start_byte() == range.start_byte
2770                && recovered_fragmented_plain_class_has_body(node, source, identifier, range))
2771        {
2772            return Some(true);
2773        }
2774        // Macro-decorated exported classes are recovered from a malformed
2775        // function_definition/declaration wrapper. Their indexed class range starts at
2776        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
2777        // may also extend the indexed range beyond the wrapper through trailing class
2778        // fragments. Match the structured container that owns the range start by its
2779        // recovered name instead of requiring identical boundaries.
2780        if node.start_byte() <= range.start_byte
2781            && range.start_byte < node.end_byte()
2782            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
2783        {
2784            if has_body {
2785                return Some(true);
2786            }
2787            saw_forward = true;
2788            continue;
2789        }
2790        let mut cursor = node.walk();
2791        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
2792    }
2793    saw_forward.then_some(false)
2794}
2795
2796/// Whether `node` is the base type displaced into the declarator field of an
2797/// export-macro class that tree-sitter represented as a declaration or
2798/// function definition.
2799///
2800/// Declaration extraction already recovers this exact malformed envelope as a
2801/// class and records the declarator as its base. Reference extraction must use
2802/// the same structural fact instead of treating the node as a function name.
2803pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
2804    if !matches!(
2805        node.kind(),
2806        "qualified_identifier" | "scoped_type_identifier" | "template_type"
2807    ) {
2808        return false;
2809    }
2810    if let Some(function) = node.parent().filter(|parent| {
2811        parent.kind() == "function_definition"
2812            && parent
2813                .child_by_field_name("declarator")
2814                .is_some_and(|declarator| same_node(declarator, node))
2815    }) {
2816        return recover_exported_class_function_definition(function, source)
2817            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
2818    }
2819    let Some(initializer) = node.parent().filter(|parent| {
2820        parent.kind() == "init_declarator"
2821            && parent
2822                .child_by_field_name("declarator")
2823                .is_some_and(|declarator| same_node(declarator, node))
2824    }) else {
2825        return false;
2826    };
2827    initializer
2828        .parent()
2829        .filter(|parent| parent.kind() == "declaration")
2830        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
2831        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
2832}
2833
2834/// Recover the class item from a region reparse that still carries the
2835/// sentinel's synthetic function envelope.  An unknown class attribute can
2836/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
2837/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
2838/// then nested below that function, so direct class-child lookup is not enough.
2839struct CppSentinelReparsedClass<'tree> {
2840    declaration_node: Node<'tree>,
2841    name: String,
2842    body: Node<'tree>,
2843    raw_supertypes: Option<Vec<String>>,
2844}
2845
2846fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
2847    let mut cursor = root.walk();
2848    root.named_children(&mut cursor)
2849        .find(|child| child.kind() != "comment")
2850        .filter(|child| child.kind() == "template_declaration")
2851}
2852
2853fn cpp_sentinel_reparsed_class<'tree>(
2854    root: Node<'tree>,
2855    template_node: Option<Node<'tree>>,
2856    source: &str,
2857    ancestry: &ParentIndex<'tree>,
2858) -> Option<CppSentinelReparsedClass<'tree>> {
2859    let container = template_node.unwrap_or(root);
2860    let mut cursor = container.walk();
2861    for child in container.named_children(&mut cursor) {
2862        if matches!(
2863            child.kind(),
2864            "class_specifier" | "struct_specifier" | "union_specifier"
2865        ) {
2866            let name = class_like_name(child, source, ancestry)?;
2867            let body = cpp_body_node(child)?;
2868            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
2869                .then(|| extract_cpp_supertypes(child, source));
2870            return Some(CppSentinelReparsedClass {
2871                declaration_node: child,
2872                name,
2873                body,
2874                raw_supertypes,
2875            });
2876        }
2877        if child.kind() == "declaration"
2878            && let Some(class_node) = first_class_like_child(child)
2879        {
2880            let name = class_like_name(class_node, source, ancestry)?;
2881            let body = cpp_body_node(class_node)?;
2882            let raw_supertypes =
2883                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2884                    .then(|| extract_cpp_supertypes(class_node, source));
2885            return Some(CppSentinelReparsedClass {
2886                declaration_node: class_node,
2887                name,
2888                body,
2889                raw_supertypes,
2890            });
2891        }
2892        // Only when the nested class item carries its own body. A bodyless
2893        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
2894        // a function definition -- is the export-macro shape recovered by the
2895        // next arm, and must fall through to it rather than abort the search.
2896        if child.kind() == "function_definition"
2897            && let Some(class_node) = first_class_like_child(child)
2898            && let Some(body) = cpp_body_node(class_node)
2899            && let Some(name) = class_like_name(class_node, source, ancestry)
2900        {
2901            let raw_supertypes =
2902                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
2903                    .then(|| extract_cpp_supertypes(class_node, source));
2904            return Some(CppSentinelReparsedClass {
2905                declaration_node: class_node,
2906                name,
2907                body,
2908                raw_supertypes,
2909            });
2910        }
2911        if child.kind() == "function_definition"
2912            && let Some((_, name, raw_supertypes)) =
2913                recover_exported_class_function_definition(child, source)
2914        {
2915            let body = cpp_body_node(child)?;
2916            return Some(CppSentinelReparsedClass {
2917                declaration_node: child,
2918                name,
2919                body,
2920                raw_supertypes,
2921            });
2922        }
2923    }
2924    None
2925}
2926
2927fn recovered_postfix_export_macro_base(
2928    node: Node<'_>,
2929    type_node: Node<'_>,
2930    declarator: Node<'_>,
2931    source: &str,
2932) -> Option<String> {
2933    let mut cursor = node.walk();
2934    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
2935        child.kind() == "ERROR"
2936            && child.start_byte() >= type_node.end_byte()
2937            && child.end_byte() <= declarator.start_byte()
2938            && postfix_export_macro_inheritance(*child, source)
2939    });
2940    malformed_clauses.next()?;
2941    if malformed_clauses.next().is_some() {
2942        return None;
2943    }
2944    recovered_malformed_base_name(declarator, source)
2945}
2946
2947fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
2948    let mut macro_count = 0;
2949    let mut colon_count = 0;
2950    let mut access_count = 0;
2951    for index in 0..node.child_count() {
2952        let Some(child) = node.child(index) else {
2953            return false;
2954        };
2955        match child.kind() {
2956            "identifier" | "type_identifier" if child.is_named() => {
2957                let candidate = normalize_cpp_whitespace(node_text(child, source));
2958                if !cpp_export_macro_token(&candidate) {
2959                    return false;
2960                }
2961                macro_count += 1;
2962            }
2963            ":" if !child.is_named() => colon_count += 1,
2964            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
2965            _ => return false,
2966        }
2967    }
2968    macro_count == 1 && colon_count == 1 && access_count == 1
2969}
2970
2971fn recovered_single_base_after_declarator(
2972    node: Node<'_>,
2973    declarator: Node<'_>,
2974    source: &str,
2975) -> Option<String> {
2976    let body_start = node
2977        .child_by_field_name("body")
2978        .map(|body| body.start_byte())
2979        .unwrap_or(node.end_byte());
2980    let mut cursor = node.walk();
2981    let mut bases = node
2982        .named_children(&mut cursor)
2983        .filter(|child| {
2984            child.kind() == "ERROR"
2985                && child.start_byte() >= declarator.end_byte()
2986                && child.end_byte() <= body_start
2987        })
2988        .filter_map(|error| displaced_exported_class_name(error, source));
2989    let base = bases.next()?;
2990    bases.next().is_none().then_some(base)
2991}
2992
2993fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
2994    (0..node.child_count()).any(|index| {
2995        node.child(index)
2996            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
2997    })
2998}
2999
3000pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3001    recover_exported_class_function_definition(node, source).is_some()
3002}
3003
3004fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3005    matches!(
3006        kind,
3007        "ERROR"
3008            | "preproc_if"
3009            | "preproc_ifdef"
3010            | "preproc_ifndef"
3011            | "preproc_else"
3012            | "preproc_elif"
3013    ) || (kind == "labeled_statement" && in_class_scope)
3014}
3015
3016pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3017    if node.kind() != "declaration" {
3018        return false;
3019    }
3020    let mut ancestor = node.parent();
3021    while let Some(container) = ancestor {
3022        match container.kind() {
3023            "compound_statement" => {
3024                return container.parent().is_some_and(|class_container| {
3025                    is_recovered_exported_class_container(class_container, source)
3026                });
3027            }
3028            // These containers preserve ScopeInfo in visit_node. declaration_list is
3029            // the body container selected for a linkage specification.
3030            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3031            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3032            _ => return false,
3033        }
3034        ancestor = container.parent();
3035    }
3036    false
3037}
3038
3039pub fn recovered_exported_class_has_body(
3040    node: Node<'_>,
3041    source: &str,
3042    expected_name: &str,
3043) -> Option<bool> {
3044    match node.kind() {
3045        "function_definition" => {
3046            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3047            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3048        }
3049        "declaration" | "field_declaration" => {
3050            let recovered = recover_exported_class_declaration(node, source)?;
3051            (recovered.name == expected_name).then(|| recovered.body.is_some())
3052        }
3053        _ => None,
3054    }
3055}
3056
3057fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3058    let body_start = node
3059        .child_by_field_name("body")
3060        .map(|body| body.start_byte())
3061        .unwrap_or(node.end_byte());
3062    let mut stack = Vec::new();
3063    for index in (0..node.named_child_count()).rev() {
3064        let Some(child) = node.named_child(index) else {
3065            continue;
3066        };
3067        if child.start_byte() >= body_start {
3068            continue;
3069        }
3070        stack.push(child);
3071    }
3072
3073    let mut best = None;
3074    while let Some(current) = stack.pop() {
3075        if matches!(current.kind(), "identifier" | "type_identifier") {
3076            let name = normalize_cpp_whitespace(node_text(current, source));
3077            if !name.is_empty()
3078                && !cpp_export_macro_token(&name)
3079                && !matches!(name.as_str(), "class" | "struct" | "union")
3080            {
3081                best = Some(name);
3082            }
3083            continue;
3084        }
3085
3086        for index in (0..current.named_child_count()).rev() {
3087            if let Some(child) = current.named_child(index)
3088                && child.start_byte() < body_start
3089            {
3090                stack.push(child);
3091            }
3092        }
3093    }
3094    best
3095}
3096
3097fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3098    if node.kind() == "declaration"
3099        && node
3100            .child_by_field_name("type")
3101            .or_else(|| first_class_like_child(node))
3102            .is_some_and(|type_node| {
3103                matches!(
3104                    type_node.kind(),
3105                    "class_specifier" | "struct_specifier" | "union_specifier"
3106                )
3107            })
3108        && let Some(name) = node
3109            .child_by_field_name("declarator")
3110            .and_then(|declarator| declarator_name_from_node(declarator, source))
3111        && !cpp_export_macro_token(&name)
3112    {
3113        return Some(name);
3114    }
3115
3116    if node.kind() == "function_definition"
3117        && node.child_by_field_name("type").is_some_and(|type_node| {
3118            matches!(
3119                type_node.kind(),
3120                "class_specifier" | "struct_specifier" | "union_specifier"
3121            )
3122        })
3123        && let Some(name) = node
3124            .child_by_field_name("declarator")
3125            .and_then(|declarator| direct_identifier_name(declarator, source))
3126        && !cpp_export_macro_token(&name)
3127    {
3128        return Some(name);
3129    }
3130
3131    let class_node = if matches!(
3132        node.kind(),
3133        "class_specifier" | "struct_specifier" | "union_specifier"
3134    ) {
3135        node
3136    } else {
3137        first_class_like_child(node)?
3138    };
3139    class_like_name_from_children(class_node, source)
3140}
3141
3142fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3143    if !matches!(
3144        node.kind(),
3145        "identifier" | "field_identifier" | "type_identifier"
3146    ) {
3147        return None;
3148    }
3149    let name = normalize_cpp_whitespace(node_text(node, source));
3150    (!name.is_empty()).then_some(name)
3151}
3152
3153fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3154    match node.kind() {
3155        "identifier" | "field_identifier" | "type_identifier" => {
3156            let name = normalize_cpp_whitespace(node_text(node, source));
3157            (!name.is_empty()).then_some(name)
3158        }
3159        _ => {
3160            let mut cursor = node.walk();
3161            node.named_children(&mut cursor)
3162                .find_map(|child| declarator_name_from_node(child, source))
3163        }
3164    }
3165}
3166
3167fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3168    let mut cursor = node.walk();
3169    node.named_children(&mut cursor).find(|child| {
3170        matches!(
3171            child.kind(),
3172            "class_specifier" | "struct_specifier" | "union_specifier"
3173        )
3174    })
3175}
3176
3177/// Push a container's children as a `Siblings` cursor rather than snapshotting
3178/// them all with one shared scope: children are visited one at a time so a
3179/// `using namespace X;` sibling can affect the scope threaded to the siblings
3180/// that textually follow it (issue #1093).
3181fn push_cpp_container_work<'tree>(
3182    node: Node<'tree>,
3183    scope: ScopeInfo,
3184    stack: &mut Vec<CppWork<'tree>>,
3185) {
3186    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3187}
3188
3189/// Materialize one selected named-child range with a tree-sitter cursor. The
3190/// cursor advances linearly across the parent's concrete children; repeatedly
3191/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3192fn push_cpp_sibling_range<'tree>(
3193    parent: Node<'tree>,
3194    start_index: usize,
3195    end_index: usize,
3196    scope: ScopeInfo,
3197    stack: &mut Vec<CppWork<'tree>>,
3198) {
3199    let mut cursor = parent.walk();
3200    let children = parent
3201        .named_children(&mut cursor)
3202        .skip(start_index)
3203        .take(end_index.saturating_sub(start_index))
3204        .collect::<Vec<_>>()
3205        .into_iter();
3206    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3207}
3208
3209/// Advance a `Siblings` cursor by one child: dispatch the current child under
3210/// the scope accumulated from its *earlier* siblings, then push a
3211/// continuation for the remaining siblings carrying the scope updated for
3212/// *this* child (only `using namespace X;` directives change it). Pushing the
3213/// continuation before the current child's own node work means the current
3214/// child's subtree fully drains (LIFO) before the next sibling is visited,
3215/// preserving left-to-right order.
3216fn advance_cpp_siblings<'tree>(
3217    mut siblings: CppSiblingsWork<'tree>,
3218    source: &str,
3219    stack: &mut Vec<CppWork<'tree>>,
3220) {
3221    let Some(child) = siblings.children.next() else {
3222        return;
3223    };
3224    let current_scope = siblings.scope.clone();
3225    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3226        siblings.scope.visible_using_namespaces.push(namespace);
3227    }
3228    if !siblings.children.as_slice().is_empty() {
3229        stack.push(CppWork::Siblings(siblings));
3230    }
3231    stack.push(CppWork::Node(CppNodeWork {
3232        node: child,
3233        scope: current_scope,
3234    }));
3235}
3236
3237/// The namespace target of a `using namespace X;` directive, or `None` for
3238/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3239/// kind. Distinguished structurally by the presence of the grammar's literal
3240/// `namespace` keyword token among the node's children -- not by inspecting
3241/// source text -- so it never misreads a member-importing using-declaration
3242/// as a namespace directive.
3243fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3244    if node.kind() != "using_declaration" {
3245        return None;
3246    }
3247    let mut cursor = node.walk();
3248    let is_namespace_directive = node
3249        .children(&mut cursor)
3250        .any(|child| child.kind() == "namespace");
3251    if !is_namespace_directive {
3252        return None;
3253    }
3254    let target = node.named_child(0)?;
3255    // A leading `::` is the explicit-global marker, not part of the namespace
3256    // path (`using namespace ::std::chrono;`). Drop that AST token before
3257    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3258    // keeps: storing the marker verbatim desynced the legacy package string from
3259    // the FqName bridge, which splits on `::` and drops the empty leading
3260    // component, tripping the package/short boundary assert when a bare-owner
3261    // out-of-line definition borrowed the directive's namespace (#1093 path).
3262    let start = target
3263        .child(0)
3264        .filter(|child| !child.is_named() && child.kind() == "::")
3265        .map_or(target.start_byte(), |marker| marker.end_byte());
3266    let text = normalize_cpp_whitespace(
3267        source
3268            .get(start..target.end_byte())
3269            .expect("using-directive target covers one source range"),
3270    );
3271    (!text.is_empty()).then_some(text)
3272}
3273
3274/// Every `using namespace X;` directive target in a file, in source order, for
3275/// resolution-time consumers that need the file's using-directives without the
3276/// per-position scope threading extraction does. Parses `source` fresh and
3277/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3278/// keys on the grammar's `namespace` keyword token, not source text), so it
3279/// never misreads a member-importing `using X::Y;` as a namespace directive.
3280///
3281/// This is a whole-file over-approximation of what is in scope at any one point
3282/// (a directive nested inside a `namespace {}` block or a function body is still
3283/// reported), which is exactly what the #1134 identity reconciler wants: extra
3284/// candidate namespaces that no visible class confirms are harmless, and two
3285/// that both confirm are treated as a genuine ambiguity by the reconciler.
3286pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3287    let mut parser = Parser::new();
3288    if parser
3289        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3290        .is_err()
3291    {
3292        return Vec::new();
3293    }
3294    let Some(tree) = parser.parse(source, None) else {
3295        return Vec::new();
3296    };
3297    let mut namespaces = Vec::new();
3298    let mut seen = std::collections::HashSet::new();
3299    let mut stack = vec![tree.root_node()];
3300    while let Some(node) = stack.pop() {
3301        if let Some(namespace) = cpp_using_namespace_target(node, source)
3302            && seen.insert(namespace.clone())
3303        {
3304            namespaces.push(namespace);
3305        }
3306        let mut cursor = node.walk();
3307        stack.extend(node.named_children(&mut cursor));
3308    }
3309    namespaces
3310}
3311
3312pub struct CppVisitor<'a> {
3313    pub file: &'a ProjectFile,
3314    pub source: &'a str,
3315    pub parsed: &'a mut ParsedFile,
3316    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3317    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3318    ///
3319    /// C has no nested tag scope: a struct/union/enum tag declared inside
3320    /// another aggregate's member list has the scope of the outer declaration
3321    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3322    /// therefore declares a file-scope `inner` that a later file-scope
3323    /// `struct inner *p;` legitimately references, where C++ would make the
3324    /// same shape a nested class `outer::inner`. Headers carry no compilation
3325    /// language of their own and keep the conservative C++ interpretation.
3326    pub c_tag_semantics: bool,
3327    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3328    /// Byte regions whose contents were re-owned by a fragmented export-class
3329    /// recovery (#938): the scattered members between the fragmented
3330    /// declaration and its displaced closing brace are indexed as members of
3331    /// the recovered class by the region reparse, so the ordinary sibling walk
3332    /// must not ALSO index them as top-level declarations (that double-indexing
3333    /// made a scattered nested class ambiguous between `Inner` and
3334    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3335    /// linear scan at visit time is fine.
3336    pub consumed_fragment_regions: Vec<(usize, usize)>,
3337    /// The namespaces tree-sitter's error recovery closed early, by the byte
3338    /// regions of the declarations it left outside them (issue #1537). The
3339    /// container walk reads a declaration's package from its parsed ancestors,
3340    /// which for those declarations stop short; see
3341    /// [`CppVisitor::recovered_namespace_scope`].
3342    pub orphaned_namespaces: OrphanedNamespaceScopeIndex,
3343    /// The namespace forward declarations already folded out of each tree this
3344    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3345    /// Empty until the first question, which the overwhelming majority of files
3346    /// never ask.
3347    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3348    /// Which owners already have field declarations in the parse product, as
3349    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3350    /// the first enum asks, which most files never do (#2786).
3351    pub field_owners: Option<CppFieldOwnerIndex>,
3352    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3353    /// happen to the declaration set, innermost last. Empty outside a recovery
3354    /// reparse, which is almost always (#2787).
3355    pub recovery_captures: Vec<CppRecoveryCapture>,
3356    /// Object-like field-list macros defined earlier in this source. Their
3357    /// replacements are parsed structurally and materialized under each
3358    /// invoking aggregate; no source-text expansion is used.
3359    pub object_macro_fields: HashMap<String, Vec<MacroReplacementField>>,
3360    /// Names whose active replacement is not a unique structured field list.
3361    /// A later `#undef` resets the ambiguity; another `#define` does not.
3362    pub ambiguous_object_macro_fields: HashSet<String>,
3363}
3364
3365/// One source-order event from an object-like field-list macro environment.
3366///
3367/// The event stream lets an include-closure consumer preserve preprocessor
3368/// invalidation without guessing which parsed header happened to be visited
3369/// first. A conservative consumer may permanently block a name after an
3370/// `undef` when conditional include order is not provable.
3371#[derive(Clone, Debug, PartialEq, Eq)]
3372pub enum ObjectMacroFieldEvent {
3373    Define {
3374        name: String,
3375        fields: Vec<MacroReplacementField>,
3376        conditional: bool,
3377    },
3378    Undef {
3379        name: String,
3380        conditional: bool,
3381    },
3382}
3383
3384/// Collect object-like field-list macros from a parsed source in source order.
3385/// Macros with conflicting active replacements are omitted so an include
3386/// closure cannot silently choose one guarded definition over another.
3387pub fn collect_cpp_object_macro_fields<'tree>(
3388    root: Node<'tree>,
3389    source: &str,
3390) -> HashMap<String, Vec<MacroReplacementField>> {
3391    let mut fields = HashMap::default();
3392    let mut ambiguous = HashSet::default();
3393    for event in collect_cpp_object_macro_field_events(root, source) {
3394        match event {
3395            ObjectMacroFieldEvent::Define {
3396                name,
3397                fields: value,
3398                conditional,
3399            } => {
3400                if value.is_empty() {
3401                    fields.remove(&name);
3402                    if conditional {
3403                        ambiguous.insert(name);
3404                    } else {
3405                        ambiguous.remove(&name);
3406                    }
3407                } else if ambiguous.contains(&name) {
3408                    // Keep the name blocked until an explicit undef resets it.
3409                } else if let Some(previous) = fields.get(&name) {
3410                    if previous != &value {
3411                        fields.remove(&name);
3412                        ambiguous.insert(name);
3413                    }
3414                } else {
3415                    fields.insert(name, value);
3416                }
3417            }
3418            ObjectMacroFieldEvent::Undef { name, conditional } => {
3419                fields.remove(&name);
3420                if conditional {
3421                    ambiguous.insert(name);
3422                } else {
3423                    ambiguous.remove(&name);
3424                }
3425            }
3426        }
3427    }
3428    fields
3429}
3430
3431/// Collect object-like field-list macro events in source order. Conditional
3432/// branches remain visible in the event stream: callers that cannot prove
3433/// branch selection can invalidate names conservatively instead of inventing
3434/// a replacement from one branch.
3435pub fn collect_cpp_object_macro_field_events<'tree>(
3436    root: Node<'tree>,
3437    source: &str,
3438) -> Vec<ObjectMacroFieldEvent> {
3439    let mut events = Vec::new();
3440    let mut stack = vec![root];
3441    while let Some(node) = stack.pop() {
3442        if node.kind() == "preproc_def"
3443            && let Some(name) = extract_macro_name(node, source)
3444        {
3445            let fields = node
3446                .child_by_field_name("value")
3447                .map(|value| {
3448                    crate::graph::syntax::object_macro_replacement_fields(node_text(value, source))
3449                })
3450                .unwrap_or_default();
3451            events.push(ObjectMacroFieldEvent::Define {
3452                name,
3453                fields,
3454                conditional: inside_preprocessor_conditional(node),
3455            });
3456        } else if is_cpp_undef_directive(node, source)
3457            && let Some(argument) = node.child_by_field_name("argument")
3458        {
3459            events.push(ObjectMacroFieldEvent::Undef {
3460                name: node_text(argument, source).trim().to_string(),
3461                conditional: inside_preprocessor_conditional(node),
3462            });
3463        }
3464        let mut cursor = node.walk();
3465        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3466        stack.extend(children.into_iter().rev());
3467    }
3468    events
3469}
3470
3471fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3472    let mut current = node.parent();
3473    while let Some(parent) = current {
3474        if matches!(
3475            parent.kind(),
3476            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3477        ) {
3478            return true;
3479        }
3480        current = parent.parent();
3481    }
3482    false
3483}
3484
3485fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3486    node.kind() == "preproc_call"
3487        && node
3488            .child_by_field_name("directive")
3489            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3490}
3491
3492impl<'a> CppVisitor<'a> {
3493    /// Records `code_unit` with the answers the walk carries forward, then adds
3494    /// it to the parse product.
3495    ///
3496    /// Every declaration this walk publishes goes through this family, so the
3497    /// carried-forward answers see each one exactly once: the field ownership
3498    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
3499    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
3500    fn add_declaration(
3501        &mut self,
3502        code_unit: CodeUnit,
3503        node: Node<'_>,
3504        parent: Option<CodeUnit>,
3505        top_level: Option<CodeUnit>,
3506    ) {
3507        self.note_declaration(&code_unit);
3508        let source = self.source;
3509        self.parsed
3510            .add_code_unit(code_unit, node, source, parent, top_level);
3511    }
3512
3513    /// Range-based form of [`Self::add_declaration`].
3514    fn add_declaration_with_range(
3515        &mut self,
3516        code_unit: CodeUnit,
3517        range: Range,
3518        parent: Option<CodeUnit>,
3519        top_level: Option<CodeUnit>,
3520    ) {
3521        self.note_declaration(&code_unit);
3522        self.parsed
3523            .add_code_unit_with_range(code_unit, range, parent, top_level);
3524    }
3525
3526    /// Deferred-replacement form of [`Self::add_declaration`].
3527    fn replace_declaration_deferred(
3528        &mut self,
3529        code_unit: CodeUnit,
3530        node: Node<'_>,
3531        parent: Option<CodeUnit>,
3532        top_level: Option<CodeUnit>,
3533    ) {
3534        self.note_replaced_declaration(&code_unit);
3535        let source = self.source;
3536        self.parsed
3537            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3538    }
3539
3540    /// Range-based form of [`Self::replace_declaration_deferred`].
3541    fn replace_declaration_with_range_deferred(
3542        &mut self,
3543        code_unit: CodeUnit,
3544        range: Range,
3545        parent: Option<CodeUnit>,
3546        top_level: Option<CodeUnit>,
3547    ) {
3548        self.note_replaced_declaration(&code_unit);
3549        self.parsed
3550            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3551    }
3552
3553    /// Notes one declaration about to enter the parse product.
3554    ///
3555    /// A declaration the product already holds is not a creation, so an open
3556    /// recovery capture ignores it -- which is the membership test the set
3557    /// difference it replaces performed. A creation inside a nested recovery
3558    /// belongs to the recoveries around it too, so every open capture takes it.
3559    fn note_declaration(&mut self, code_unit: &CodeUnit) {
3560        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3561            for capture in &mut self.recovery_captures {
3562                if capture.removed_pre_existing.contains(code_unit) {
3563                    continue;
3564                }
3565                if capture.created_units.insert(code_unit.clone()) {
3566                    capture.created.push(code_unit.clone());
3567                }
3568            }
3569        }
3570        if let Some(field_owners) = self.field_owners.as_mut() {
3571            field_owners.record(code_unit, self.file);
3572        }
3573    }
3574
3575    /// Notes one declaration about to replace an existing one.
3576    ///
3577    /// A deferred replacement of a declaration that already owns children
3578    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
3579    /// a removal is the one thing the field index cannot absorb by addition.
3580    /// Drop it; the next question rebuilds it from the declarations that
3581    /// survive. A replacement of a unit with no children, and a "replacement"
3582    /// of a unit that is not there at all, remove nothing.
3583    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3584        let removes_children = self.parsed.contains_declaration(code_unit)
3585            && self
3586                .parsed
3587                .children
3588                .get(code_unit)
3589                .is_some_and(|children| !children.is_empty());
3590        if removes_children {
3591            if !self.recovery_captures.is_empty() {
3592                let removed = self.declarations_a_replacement_removes(code_unit);
3593                for capture in &mut self.recovery_captures {
3594                    for unit in &removed {
3595                        // A declaration this capture watched being created is
3596                        // its own; one it did not is a declaration that was
3597                        // already there when the capture opened, so creating it
3598                        // again is a restoration and not a mint.
3599                        if !capture.created_units.contains(unit) {
3600                            capture.removed_pre_existing.insert(unit.clone());
3601                        }
3602                    }
3603                }
3604            }
3605            self.field_owners = None;
3606        }
3607        self.note_declaration(code_unit);
3608    }
3609
3610    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
3611    /// when `code_unit` is replaced: its children, transitively.
3612    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
3613        let mut removed = Vec::new();
3614        let mut seen = HashSet::default();
3615        let mut pending: Vec<CodeUnit> = self
3616            .parsed
3617            .children
3618            .get(code_unit)
3619            .cloned()
3620            .unwrap_or_default();
3621        while let Some(unit) = pending.pop() {
3622            if !seen.insert(unit.clone()) {
3623                continue;
3624            }
3625            if let Some(children) = self.parsed.children.get(&unit) {
3626                pending.extend(children.iter().cloned());
3627            }
3628            removed.push(unit);
3629        }
3630        removed
3631    }
3632
3633    fn visit_function_like_export_class_pair<'tree>(
3634        &mut self,
3635        node: Node<'tree>,
3636        scope: &ScopeInfo,
3637        stack: &mut Vec<CppWork<'tree>>,
3638        ancestry: &ParentIndex<'tree>,
3639    ) -> bool {
3640        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
3641            return false;
3642        };
3643        let member_outcome = self
3644            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
3645        // A malformed class body can escape into several following siblings
3646        // before the next export-macro class head appears. Inspect siblings in
3647        // order and stop at the first envelope that contains such a head. One
3648        // envelope can contain several following classes, all recovered in a
3649        // single bounded traversal.
3650        let mut displaced = node.next_named_sibling();
3651        while let Some(candidate) = displaced {
3652            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
3653                break;
3654            }
3655            displaced = candidate.next_named_sibling();
3656        }
3657        let class_unit = self.visit_named_class_like_shape(
3658            node,
3659            recovered.name,
3660            // The adjacent initializer_list proves the class body envelope,
3661            // but its children are expression-shaped rather than declaration-
3662            // preserving. Index the class identity here; callable definitions
3663            // remain available from their ordinary out-of-line declarations.
3664            None,
3665            true,
3666            Some(recovered.range),
3667            recovered.raw_supertypes,
3668            scope,
3669            stack,
3670            ancestry,
3671        );
3672        self.parsed
3673            .record_materialization(MaterializationRecord::RecoveredDeclaration {
3674                recovery: recovered.range,
3675                unit: class_unit.clone(),
3676            });
3677        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3678            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3679                tree.root_node(),
3680                class_unit.identifier(),
3681                self.source,
3682            )
3683        {
3684            self.visit_recovered_fragment_constructor(
3685                range,
3686                body,
3687                node,
3688                &class_unit,
3689                scope,
3690                ancestry,
3691            );
3692        }
3693        if let Some(outcome) = member_outcome {
3694            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3695        }
3696        self.consumed_fragment_regions
3697            .push((node.start_byte(), recovered.range.end_byte));
3698        true
3699    }
3700
3701    fn visit_embedded_function_like_export_classes<'tree>(
3702        &mut self,
3703        node: Node<'tree>,
3704        scope: &ScopeInfo,
3705        stack: &mut Vec<CppWork<'tree>>,
3706        ancestry: &ParentIndex<'tree>,
3707    ) -> bool {
3708        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
3709        let found = !recovered_classes.is_empty();
3710        for recovered in recovered_classes {
3711            let member_outcome = self.reparse_fragmented_export_class_members(
3712                &recovered.fragmented_body,
3713                &recovered.name,
3714            );
3715            let class_unit = self.visit_named_class_like_shape(
3716                node,
3717                recovered.name,
3718                None,
3719                true,
3720                Some(recovered.range),
3721                Some(recovered.raw_supertypes),
3722                scope,
3723                stack,
3724                ancestry,
3725            );
3726            self.parsed
3727                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3728                    recovery: recovered.range,
3729                    unit: class_unit.clone(),
3730                });
3731            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
3732                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
3733                    tree.root_node(),
3734                    class_unit.identifier(),
3735                    self.source,
3736                )
3737            {
3738                self.visit_recovered_fragment_constructor(
3739                    range,
3740                    body,
3741                    node,
3742                    &class_unit,
3743                    scope,
3744                    ancestry,
3745                );
3746            }
3747            if let Some(outcome) = member_outcome {
3748                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
3749            }
3750        }
3751        found
3752    }
3753
3754    /// Walk `node`'s container, answering every ancestor question from
3755    /// `ancestry`.
3756    ///
3757    /// `ancestry` must index the tree `node` belongs to. The caller owns it
3758    /// because one tree can be walked more than once -- a header's C and C++
3759    /// readings are the same tree under different tag semantics -- and the
3760    /// parent relation is a property of the tree, not of the reading.
3761    #[allow(clippy::too_many_arguments)]
3762    pub fn visit_container<'tree>(
3763        &mut self,
3764        node: Node<'tree>,
3765        ancestry: &ParentIndex<'tree>,
3766        package_name: &str,
3767        module: Option<CodeUnit>,
3768        class_unit: Option<CodeUnit>,
3769        template_signature: Option<String>,
3770        visible_using_namespaces: Vec<String>,
3771    ) {
3772        let scope = ScopeInfo {
3773            package_name: package_name.to_string(),
3774            module,
3775            class_unit,
3776            template_signature,
3777            template_metadata: None,
3778            declarations_are_fields: false,
3779            recovered_specialization_member_scope: false,
3780            visible_using_namespaces,
3781        };
3782        self.run_container_work(node, scope, ancestry);
3783    }
3784
3785    /// Whether a work node lies entirely inside a byte region consumed by a
3786    /// fragmented export-class recovery (#938); such nodes were already indexed
3787    /// as members of the recovered class by the region reparse.
3788    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
3789        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
3790    }
3791
3792    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
3793    /// candidate a recovery is about to reparse but has not yet turned into a
3794    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
3795    /// prior structured recovery.
3796    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
3797        self.consumed_fragment_regions
3798            .iter()
3799            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
3800    }
3801
3802    /// Drive the container work loop from an explicit seed scope to completion. The
3803    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
3804    /// stays alive for the whole traversal.
3805    ///
3806    /// Every ancestor question this walk asks is answered from `ancestry`, which
3807    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
3808    /// node's position in the tree, which made a generated header with thousands
3809    /// of top-level declarations quadratic (#2361). The index is the caller's
3810    /// because it outlives any one walk: the file's tree is walked twice when a
3811    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
3812    /// builds its own index for its own tree.
3813    fn run_container_work<'tree>(
3814        &mut self,
3815        node: Node<'tree>,
3816        scope: ScopeInfo,
3817        ancestry: &ParentIndex<'tree>,
3818    ) {
3819        self.drain_cpp_work(
3820            vec![CppWork::Container(CppContainer { node, scope })],
3821            ancestry,
3822        );
3823    }
3824
3825    /// The work loop itself, from whatever seed the caller built.
3826    ///
3827    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
3828    /// that must walk only part of a reparsed tree seeds it with the sibling
3829    /// range it may walk instead, which keeps the `using namespace X;` scope
3830    /// accumulation `advance_cpp_siblings` performs.
3831    fn drain_cpp_work<'tree>(
3832        &mut self,
3833        mut stack: Vec<CppWork<'tree>>,
3834        ancestry: &ParentIndex<'tree>,
3835    ) {
3836        while let Some(work) = stack.pop() {
3837            match work {
3838                CppWork::Container(container) => {
3839                    push_cpp_container_work(container.node, container.scope, &mut stack);
3840                }
3841                CppWork::Siblings(siblings) => {
3842                    advance_cpp_siblings(siblings, self.source, &mut stack);
3843                }
3844                CppWork::Node(work) => {
3845                    if self.node_is_inside_consumed_fragment(work.node) {
3846                        continue;
3847                    }
3848                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
3849                }
3850            }
3851        }
3852    }
3853
3854    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
3855    /// it only when the entire region is member-shaped. This validation must happen
3856    /// before registering the recovered class because a rejected speculative range
3857    /// must not leak into the ordinary recovery path.
3858    fn reparse_fragmented_export_class_members(
3859        &self,
3860        fragmented: &FragmentedExportBody,
3861        class_name: &str,
3862    ) -> Option<FragmentedExportMembers> {
3863        if fragmented.reparse_start >= fragmented.reparse_end {
3864            return None;
3865        }
3866        let tree = cpp_reparse_fragmented_class_body(
3867            self.source,
3868            fragmented.reparse_start,
3869            fragmented.reparse_end,
3870        )?;
3871        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
3872            return Some(FragmentedExportMembers::Complete(tree));
3873        }
3874        let has_conditional_constructor = {
3875            let root = tree.root_node();
3876            let mut cursor = root.walk();
3877            root.named_children(&mut cursor).any(|child| {
3878                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
3879            })
3880        };
3881        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
3882    }
3883
3884    /// Index an already validated fragmented body as members of `class_unit`. The
3885    /// region reparse keeps each member's exact original byte and line positions.
3886    fn visit_fragmented_export_class_members(
3887        &mut self,
3888        outcome: FragmentedExportMembers,
3889        class_unit: CodeUnit,
3890        scope: &ScopeInfo,
3891    ) -> bool {
3892        let (tree, complete) = match outcome {
3893            FragmentedExportMembers::Complete(tree) => (tree, true),
3894            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
3895        };
3896        let root = tree.root_node();
3897        let class_name = class_unit.identifier().to_string();
3898        let member_scope = ScopeInfo {
3899            // A recovered export-macro class may borrow its namespace from an
3900            // earlier forward declaration even when the malformed node itself
3901            // sits at file scope. Use the recovered class identity as the
3902            // authoritative package for reparsed members as well.
3903            package_name: class_unit.package_name().to_string(),
3904            module: scope.module.clone(),
3905            class_unit: Some(class_unit),
3906            template_signature: scope.template_signature.clone(),
3907            template_metadata: None,
3908            declarations_are_fields: true,
3909            recovered_specialization_member_scope: false,
3910            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3911        };
3912        if !complete {
3913            // A conditional beginning immediately after an access label can
3914            // fragment one constructor declaration while leaving the rest of
3915            // the class body as unsafe statement soup. Recover only that
3916            // structurally proven constructor and leave the outer-tree
3917            // siblings unconsumed for their ordinary walk.
3918            let mut cursor = root.walk();
3919            let constructors = root
3920                .named_children(&mut cursor)
3921                .filter_map(|child| {
3922                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
3923                })
3924                .collect::<Vec<_>>();
3925            // The reparsed region is its own tree, so this drain walks it with
3926            // its own parent index.
3927            let reparsed_ancestry = ParentIndex::new(root);
3928            for constructor in constructors {
3929                let mut stack = Vec::new();
3930                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
3931                while let Some(work) = stack.pop() {
3932                    match work {
3933                        CppWork::Container(container) => {
3934                            push_cpp_container_work(container.node, container.scope, &mut stack);
3935                        }
3936                        CppWork::Siblings(siblings) => {
3937                            advance_cpp_siblings(siblings, self.source, &mut stack);
3938                        }
3939                        CppWork::Node(work) => {
3940                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
3941                        }
3942                    }
3943                }
3944            }
3945            return false;
3946        }
3947        // The reparsed region is its own tree, so this walk indexes it itself.
3948        self.run_container_work(root, member_scope, &ParentIndex::new(root));
3949        true
3950    }
3951
3952    fn visit_recovered_fragment_constructor<'tree>(
3953        &mut self,
3954        range: std::ops::Range<usize>,
3955        constructor_body: Node<'tree>,
3956        class_declaration: Node<'tree>,
3957        class_unit: &CodeUnit,
3958        scope: &ScopeInfo,
3959        ancestry: &ParentIndex<'tree>,
3960    ) {
3961        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
3962            return;
3963        };
3964        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
3965            tree.root_node(),
3966            range.start,
3967            class_unit.identifier(),
3968            self.source,
3969        ) else {
3970            return;
3971        };
3972        let member_scope = ScopeInfo {
3973            package_name: class_unit.package_name().to_string(),
3974            module: scope.module.clone(),
3975            class_unit: Some(class_unit.clone()),
3976            template_signature: scope.template_signature.clone(),
3977            template_metadata: None,
3978            declarations_are_fields: true,
3979            recovered_specialization_member_scope: false,
3980            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3981        };
3982        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
3983        else {
3984            return;
3985        };
3986        debug_assert_eq!(function.name, class_unit.identifier());
3987        let code_unit = function.code_unit(self.file.clone());
3988        self.add_declaration_with_range(
3989            code_unit.clone(),
3990            Range {
3991                start_byte: function_declarator.start_byte(),
3992                end_byte: constructor_body.end_byte(),
3993                start_line: function_declarator.start_position().row + 1,
3994                end_line: constructor_body.end_position().row + 1,
3995            },
3996            None,
3997            None,
3998        );
3999        self.parsed.add_signature_with_metadata(
4000            code_unit.clone(),
4001            cpp_signature_metadata(
4002                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4003                function_declarator,
4004                self.source,
4005                ancestry,
4006            )
4007            .with_declaration_only(false)
4008            .with_callable_linkage(cpp_callable_linkage(
4009                class_declaration,
4010                self.source,
4011                ancestry,
4012            )),
4013        );
4014        self.parsed.add_child(class_unit.clone(), code_unit);
4015    }
4016
4017    fn visit_recovered_fragment_prefix_members<'tree>(
4018        &mut self,
4019        root: Node<'tree>,
4020        constructor_start: usize,
4021        class_unit: &CodeUnit,
4022        scope: &ScopeInfo,
4023        ancestry: &ParentIndex<'tree>,
4024    ) {
4025        let member_scope = ScopeInfo {
4026            package_name: class_unit.package_name().to_string(),
4027            module: scope.module.clone(),
4028            class_unit: Some(class_unit.clone()),
4029            template_signature: scope.template_signature.clone(),
4030            template_metadata: None,
4031            declarations_are_fields: true,
4032            recovered_specialization_member_scope: false,
4033            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4034        };
4035        let mut stack = vec![root];
4036        while let Some(current) = stack.pop() {
4037            if current.kind() == "comment" || current.start_byte() >= constructor_start {
4038                continue;
4039            }
4040            if current.end_byte() <= constructor_start
4041                && current.kind() != "translation_unit"
4042                && current.kind() != "labeled_statement"
4043                && current.kind() != "ERROR"
4044            {
4045                let mut work_stack = Vec::new();
4046                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4047                while let Some(work) = work_stack.pop() {
4048                    match work {
4049                        CppWork::Container(container) => {
4050                            push_cpp_container_work(
4051                                container.node,
4052                                container.scope,
4053                                &mut work_stack,
4054                            );
4055                        }
4056                        CppWork::Siblings(siblings) => {
4057                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
4058                        }
4059                        CppWork::Node(work) => {
4060                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4061                        }
4062                    }
4063                }
4064                continue;
4065            }
4066            if matches!(
4067                current.kind(),
4068                "translation_unit" | "labeled_statement" | "ERROR"
4069            ) {
4070                let mut cursor = current.walk();
4071                stack.extend(current.named_children(&mut cursor));
4072            }
4073        }
4074    }
4075
4076    fn visit_node<'tree>(
4077        &mut self,
4078        node: Node<'tree>,
4079        scope: &ScopeInfo,
4080        stack: &mut Vec<CppWork<'tree>>,
4081        ancestry: &ParentIndex<'tree>,
4082    ) {
4083        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4084            self.visit_node(node, &recovered_scope, stack, ancestry);
4085            return;
4086        }
4087        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4088            self.visit_node(node, &recovered_scope, stack, ancestry);
4089            return;
4090        }
4091        // Fragmented-class recovery below may consume a malformed function
4092        // envelope before the ordinary kind dispatch runs. Recover any
4093        // export-macro class embedded in that envelope first; the strict class
4094        // head/base/body predicate is independent of which later recovery owns
4095        // the surrounding parser fragment.
4096        if node.kind() == "function_definition" && node.has_error() {
4097            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4098        }
4099        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
4100        {
4101            let displaced_namespace_items =
4102                displaced_fragment_namespace_geometry(node, self.source)
4103                    .map(|boundary| boundary.namespace_items)
4104                    .unwrap_or_default();
4105            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4106            let mut class_stack = Vec::new();
4107            // When the full body cannot be safely reparsed, the original class
4108            // node still proves ownership for its parser-visible prefix.
4109            let parser_visible_body =
4110                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4111                    .then(|| cpp_body_node(class_node))
4112                    .flatten();
4113            let class_unit = self.visit_named_class_like_shape(
4114                class_node,
4115                name,
4116                parser_visible_body,
4117                true,
4118                Some(fragmented.class_range),
4119                Some(extract_cpp_supertypes(class_node, self.source)),
4120                scope,
4121                &mut class_stack,
4122                ancestry,
4123            );
4124            let member_scope = ScopeInfo {
4125                package_name: class_unit.package_name().to_string(),
4126                module: scope.module.clone(),
4127                class_unit: Some(class_unit.clone()),
4128                template_signature: scope.template_signature.clone(),
4129                template_metadata: None,
4130                declarations_are_fields: true,
4131                recovered_specialization_member_scope: false,
4132                visible_using_namespaces: scope.visible_using_namespaces.clone(),
4133            };
4134            let complete = outcome.is_some_and(|outcome| {
4135                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4136            });
4137            if complete {
4138                self.consumed_fragment_regions
4139                    .push((node.start_byte(), fragmented.class_range.end_byte));
4140            } else {
4141                // A macro-constrained member can make the full body reparse
4142                // unsafe while tree-sitter still exposes later class members
4143                // as bounded siblings up to the displaced `}`/`;`. Keep the
4144                // structurally proven class/base declaration and re-own those
4145                // sibling nodes under it. They retain their original parser
4146                // nodes and exact ranges; the close boundary comes solely from
4147                // `fragmented_plain_class_body`.
4148                // Template wrappers put the escaped members beside the
4149                // template rather than beside its malformed declaration.
4150                for candidate in cpp_following_named_siblings(node, self.source) {
4151                    if candidate.start_byte() >= fragmented.reparse_end {
4152                        break;
4153                    }
4154                    if cpp_fragment_sibling_is_class_member(
4155                        candidate,
4156                        fragmented.reparse_end,
4157                        self.source,
4158                    ) {
4159                        self.recovered_class_sibling_scopes
4160                            .insert(candidate.id(), member_scope.clone());
4161                    }
4162                }
4163            }
4164            for item in displaced_namespace_items {
4165                self.recovered_class_sibling_scopes
4166                    .insert(item.id(), scope.clone());
4167            }
4168            stack.extend(class_stack);
4169            return;
4170        }
4171        match node.kind() {
4172            "template_declaration" => {
4173                if let Some(recovered) =
4174                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
4175                {
4176                    let mut template_scope = scope.clone();
4177                    template_scope.template_signature =
4178                        cpp_template_signature(node, recovered.declaration_node, self.source);
4179                    template_scope.template_metadata =
4180                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4181                    let raw_supertypes =
4182                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
4183                    let mut class_stack = Vec::new();
4184                    let class_unit = self.visit_named_class_like_shape(
4185                        recovered.class_node,
4186                        recovered.name,
4187                        Some(recovered.body),
4188                        true,
4189                        Some(recovered.range),
4190                        raw_supertypes,
4191                        &template_scope,
4192                        &mut class_stack,
4193                        ancestry,
4194                    );
4195                    self.parsed.record_materialization(
4196                        MaterializationRecord::RecoveredDeclaration {
4197                            recovery: recovered.range,
4198                            unit: class_unit.clone(),
4199                        },
4200                    );
4201                    let member_scope = ScopeInfo {
4202                        package_name: template_scope.package_name.clone(),
4203                        module: template_scope.module.clone(),
4204                        class_unit: Some(class_unit.clone()),
4205                        template_signature: template_scope.template_signature.clone(),
4206                        template_metadata: None,
4207                        declarations_are_fields: true,
4208                        recovered_specialization_member_scope: recovered
4209                            .class_node
4210                            .child_by_field_name("name")
4211                            .is_some_and(|name| name.kind() == "template_type"),
4212                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4213                    };
4214                    for tail_member in recovered.tail_members.into_iter().rev() {
4215                        stack.push(CppWork::Node(CppNodeWork {
4216                            node: tail_member,
4217                            scope: member_scope.clone(),
4218                        }));
4219                    }
4220                    stack.extend(class_stack);
4221                    for sibling in recovered.member_siblings {
4222                        self.recovered_class_sibling_scopes
4223                            .insert(sibling.id(), member_scope.clone());
4224                    }
4225                    return;
4226                }
4227                for index in (0..node.named_child_count()).rev() {
4228                    let Some(child) = node.named_child(index) else {
4229                        continue;
4230                    };
4231                    if matches!(
4232                        child.kind(),
4233                        "class_specifier"
4234                            | "struct_specifier"
4235                            | "union_specifier"
4236                            | "enum_specifier"
4237                            | "function_definition"
4238                            | "declaration"
4239                            | "field_declaration"
4240                            | "alias_declaration"
4241                            | "namespace_definition"
4242                    ) {
4243                        let mut template_scope = scope.clone();
4244                        template_scope.template_signature =
4245                            cpp_template_signature(node, child, self.source);
4246                        template_scope.template_metadata =
4247                            cpp_template_metadata(node, child, self.source, ancestry);
4248                        if let Some(recovered) = recover_fragmented_partial_specialization(
4249                            node,
4250                            child,
4251                            self.source,
4252                            ancestry,
4253                        ) {
4254                            let code_unit = self.visit_named_class_like_shape(
4255                                recovered.declaration_node,
4256                                recovered.name,
4257                                None,
4258                                true,
4259                                Some(recovered.range),
4260                                None,
4261                                &template_scope,
4262                                stack,
4263                                ancestry,
4264                            );
4265                            self.parsed.record_materialization(
4266                                MaterializationRecord::RecoveredDeclaration {
4267                                    recovery: recovered.range,
4268                                    unit: code_unit.clone(),
4269                                },
4270                            );
4271                            let mut member_scope = template_scope.clone();
4272                            member_scope.class_unit = Some(code_unit);
4273                            member_scope.declarations_are_fields = true;
4274                            member_scope.recovered_specialization_member_scope = true;
4275                            for prefix_member in recovered.prefix_members.into_iter().rev() {
4276                                stack.push(CppWork::Node(CppNodeWork {
4277                                    node: prefix_member,
4278                                    scope: member_scope.clone(),
4279                                }));
4280                            }
4281                            for sibling in recovered.member_siblings {
4282                                self.recovered_class_sibling_scopes
4283                                    .insert(sibling.id(), member_scope.clone());
4284                            }
4285                            for following in recovered.following_declarations.into_iter().rev() {
4286                                stack.push(CppWork::Node(CppNodeWork {
4287                                    node: following,
4288                                    scope: scope.clone(),
4289                                }));
4290                            }
4291                            return;
4292                        }
4293                        stack.push(CppWork::Node(CppNodeWork {
4294                            node: child,
4295                            scope: template_scope,
4296                        }));
4297                    }
4298                }
4299            }
4300            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4301            "linkage_specification" => {
4302                if let Some(body) = cpp_body_node(node) {
4303                    stack.push(CppWork::Container(CppContainer {
4304                        node: body,
4305                        scope: scope.clone(),
4306                    }));
4307                } else {
4308                    stack.push(CppWork::Container(CppContainer {
4309                        node,
4310                        scope: scope.clone(),
4311                    }));
4312                }
4313            }
4314            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4315                self.visit_class_like(node, scope, stack, ancestry)
4316            }
4317            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4318            // A bare namespace-begin sentinel can make tree-sitter promote the
4319            // wrapped declaration to an ERROR node instead of the usual bogus
4320            // function_definition envelope. Keep the recovery entry point on
4321            // the same structured path for both shapes; ordinary ERROR nodes
4322            // retain their declaration-preserving wrapper traversal when the
4323            // sentinel predicate does not match.
4324            "ERROR" => {
4325                self.visit_object_macro_error_classes(node, scope);
4326                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4327                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4328                    if self.visit_collapsed_macro_declaration_run(node, scope) {
4329                        return;
4330                    }
4331                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4332                        return;
4333                    }
4334                    self.visit_macro_swallowed_function_declarations(node, scope);
4335                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4336                    self.visit_stranded_class_members(node, scope, ancestry);
4337                    stack.push(CppWork::Container(CppContainer {
4338                        node,
4339                        scope: scope.clone(),
4340                    }));
4341                }
4342            }
4343            "declaration" => {
4344                if node.has_error() {
4345                    self.visit_prototype_macro_declarations(node, scope);
4346                    if self.node_is_inside_consumed_fragment(node) {
4347                        // The whole declaration was a recovered K&R
4348                        // prototype macro invocation (issue #2932); the
4349                        // reparse above already indexed its Function.
4350                        // Falling through to the ordinary declaration
4351                        // visitor would additionally mint the swapped-field
4352                        // wreckage this recovery exists to replace.
4353                        return;
4354                    }
4355                }
4356                if scope.class_unit.is_some()
4357                    && scope.declarations_are_fields
4358                    && scope.recovered_specialization_member_scope
4359                    && let Some(alias_name) =
4360                        recovered_using_declaration_alias_name(node, self.source)
4361                {
4362                    self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4363                } else {
4364                    self.visit_declaration(
4365                        node,
4366                        scope,
4367                        scope.declarations_are_fields,
4368                        stack,
4369                        ancestry,
4370                    )
4371                }
4372            }
4373            // A K&R prototype macro invocation with a pointer return type and a
4374            // simple argument list parses with no ERROR node at all: tree-sitter
4375            // reads `T *name _(( args ));` as a multiplication of a type by a
4376            // call (`T * name_(...)`), wrapped in an `expression_statement`
4377            // that `has_error()` only because of a `MISSING "::"` inside it
4378            // (issue #2932). Nothing else mints a declaration from an
4379            // `expression_statement` at declaration scope today, so there is
4380            // no existing behavior to preserve here if this node is not the
4381            // K&R shape; the admission gate inside
4382            // `visit_prototype_macro_declarations` (exactly one clean function
4383            // prototype spanning the recovered run) is what keeps this arm
4384            // safe on an ordinary errorful expression statement that happens
4385            // to contain nested parentheses.
4386            "expression_statement" => {
4387                if node.has_error() {
4388                    self.visit_prototype_macro_declarations(node, scope);
4389                }
4390            }
4391            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4392            "preproc_call" => self.visit_preproc_call(node, scope),
4393            "type_definition" | "alias_declaration" => {
4394                self.visit_type_declaration(node, scope, stack, ancestry)
4395            }
4396            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4397            // `#include` is collected by `collect_cpp_includes` before the
4398            // walk, so a directive the container walk never reaches -- inside
4399            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4400            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4401            // include claim.
4402            "preproc_include" => {}
4403            kind if preserves_declaration_scope_through_wrapper(
4404                kind,
4405                scope.class_unit.is_some(),
4406            ) =>
4407            {
4408                // A preprocessor conditional gates every declaration inside it
4409                // on a configuration this analyzer never evaluates; record the
4410                // interval so declaration state can say so (issue #1476). The
4411                // else/elif branches are children of the `preproc_if` node, so
4412                // recording the openers covers every branch.
4413                if kind == "labeled_statement" {
4414                    self.visit_access_label_constructor(node, scope);
4415                }
4416                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4417                    let mut range = cpp_declaration_range(node);
4418                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4419                        range.end_byte = boundary.end_byte;
4420                        range.end_line = boundary.end_line;
4421                    }
4422                    self.parsed.record_materialization(
4423                        MaterializationRecord::ConfigurationConditional { range },
4424                    );
4425                    if node.has_error() {
4426                        // A malformed export-macro class can close the namespace
4427                        // node early while the enclosing include guard still owns
4428                        // the remaining class-head/body pairs. The ordinary walk
4429                        // cannot carry the lost namespace through those promoted
4430                        // siblings. Scan only structured ERROR nodes in this
4431                        // already-malformed conditional; the pair recovery's
4432                        // exact class/macro/body predicate remains the admission
4433                        // gate, and its namespace lifting restores the owner.
4434                        let mut candidates = vec![node];
4435                        while let Some(candidate) = candidates.pop() {
4436                            // An `ERROR` still inside a namespace body is one
4437                            // the ordinary walk reaches with that namespace in
4438                            // scope. Claiming it here registers the recovered
4439                            // class at file scope and the consumed region then
4440                            // suppresses the walk that would have named it
4441                            // correctly -- which is why Botan's `DL_Group` was
4442                            // `DL_Group` and not `Botan.DL_Group` in the real
4443                            // header, where an include guard wraps the
4444                            // namespace, and was right in a fixture without one
4445                            // (#2552).
4446                            if candidate.kind() == "ERROR"
4447                                && !cpp_is_inside_namespace_body(candidate, ancestry)
4448                                && self.visit_function_like_export_class_pair(
4449                                    candidate, scope, stack, ancestry,
4450                                )
4451                            {
4452                                continue;
4453                            }
4454                            for index in (0..candidate.named_child_count()).rev() {
4455                                candidates.push(
4456                                    candidate
4457                                        .named_child(index)
4458                                        .expect("index below the node's own named child count"),
4459                                );
4460                            }
4461                        }
4462                    }
4463                }
4464                stack.push(CppWork::Container(CppContainer {
4465                    node,
4466                    scope: scope.clone(),
4467                }))
4468            }
4469            _ => {}
4470        }
4471    }
4472
4473    fn visit_macro_swallowed_function_declarations<'tree>(
4474        &mut self,
4475        envelope: Node<'tree>,
4476        scope: &ScopeInfo,
4477    ) {
4478        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4479            || envelope.kind() == "ERROR"
4480                && envelope
4481                    .parent()
4482                    .is_some_and(|parent| parent.kind() == "ERROR")
4483        {
4484            return;
4485        }
4486        let mut stack = (0..envelope.named_child_count())
4487            .filter_map(|index| envelope.named_child(index))
4488            .collect::<Vec<_>>();
4489        while let Some(node) = stack.pop() {
4490            if node.kind() == "function_declarator" {
4491                self.visit_error_swallowed_function_declaration(node, scope);
4492            }
4493            for index in 0..node.named_child_count() {
4494                if let Some(child) = node.named_child(index) {
4495                    stack.push(child);
4496                }
4497            }
4498        }
4499    }
4500
4501    /// Index the declarations an attribute-like macro invocation swallowed into
4502    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
4503    /// the shape and why the parser produces it.
4504    fn visit_macro_wrapped_declarations<'tree>(
4505        &mut self,
4506        envelope: Node<'tree>,
4507        scope: &ScopeInfo,
4508        ancestry: &ParentIndex<'tree>,
4509    ) {
4510        let recovered = macro_wrapped_declarations(envelope, self.source);
4511        if recovered.is_empty() {
4512            return;
4513        }
4514        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4515        self.record_recovered_declarations(recovery, |visitor| {
4516            for declaration in recovered {
4517                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4518            }
4519        });
4520    }
4521
4522    /// Index the declarations a macro invocation collapsed into one envelope.
4523    /// See [`collapsed_macro_declaration_run`] for the shape and why the parser
4524    /// produces it. Returns whether it claimed `envelope`.
4525    ///
4526    /// The envelope's own nodes cannot be read the way the swallowed tail of
4527    /// the one-line shape can. Of the 214 declarations whisper's `llama.h`
4528    /// hides in it, 57 are shredded to bare identifier and punctuation tokens
4529    /// with no declarator left at all, and the declarators that do survive on
4530    /// the envelope's declarator spine pair one declaration's name with the
4531    /// *next* declaration's parameter list. Reading those would be a guess.
4532    ///
4533    /// The bytes are still ordinary declarations, though, and the collapse is
4534    /// the parser carrying the failure forward from one macro invocation. So
4535    /// reparse the envelope's region, walk the items the parser makes of it,
4536    /// and when one item is itself a collapsed run, recover that invocation
4537    /// from its own bytes and resume the scan just past it. Each pass starts
4538    /// later than the last, so the scan is a loop over the invocations that
4539    /// collapse, not over the declarations: the real header needs three passes
4540    /// for 214 declarations.
4541    fn visit_collapsed_macro_declaration_run(
4542        &mut self,
4543        envelope: Node<'_>,
4544        scope: &ScopeInfo,
4545    ) -> bool {
4546        if collapsed_macro_declaration_run(envelope, self.source).is_none() {
4547            return false;
4548        }
4549        let start = envelope.start_byte();
4550        let end = envelope.end_byte();
4551        let recovery = cpp_recovery_window(self.source, start, end);
4552        self.record_recovered_declarations(recovery, |visitor| {
4553            let mut position = start;
4554            while position < end {
4555                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
4556                    return;
4557                };
4558                let root = tree.root_node();
4559                // A region reparse is its own tree and needs its own parent
4560                // index; the caller's answers nothing about these nodes.
4561                let ancestry = ParentIndex::new(root);
4562                let mut cursor = root.walk();
4563                let collapsed =
4564                    root.named_children(&mut cursor)
4565                        .enumerate()
4566                        .find_map(|(index, item)| {
4567                            collapsed_macro_declaration_run(item, visitor.source)
4568                                .map(|run| (index, item, run))
4569                        });
4570                // Walk only the items before the collapsed one. It swallowed
4571                // the rest of the region, so handing it to the ordinary walk
4572                // would re-enter this recovery on a region that starts where
4573                // this one did.
4574                let mut stack = Vec::new();
4575                push_cpp_sibling_range(
4576                    root,
4577                    0,
4578                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
4579                    scope.clone(),
4580                    &mut stack,
4581                );
4582                visitor.drain_cpp_work(stack, &ancestry);
4583                let Some((_, item, run)) = collapsed else {
4584                    return;
4585                };
4586                // On its own bytes the invocation is the declaration-scope
4587                // shape `macro_wrapped_declarations` reads, so its wrapped
4588                // declaration needs no reader of its own here.
4589                if let Some(head) =
4590                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
4591                {
4592                    let head_root = head.root_node();
4593                    visitor.run_container_work(
4594                        head_root,
4595                        scope.clone(),
4596                        &ParentIndex::new(head_root),
4597                    );
4598                }
4599                assert!(
4600                    run.invocation_end > position,
4601                    "a collapsed run at {position} must end after the byte the scan resumed \
4602                     from, but ended at {}",
4603                    run.invocation_end
4604                );
4605                position = run.invocation_end;
4606            }
4607        });
4608        true
4609    }
4610
4611    /// Index the members a string-argument attribute macro stranded in an
4612    /// `ERROR` inside a class body.
4613    ///
4614    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
4615    /// grouping of the attributed member and of every member written after it
4616    /// until it recovers. The members are still whole `function_declarator`
4617    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
4618    /// export-macro class such as Botan's `DL_Group` was indexed with no
4619    /// members at all (#2552).
4620    fn visit_stranded_class_members<'tree>(
4621        &mut self,
4622        node: Node<'tree>,
4623        scope: &ScopeInfo,
4624        ancestry: &ParentIndex<'tree>,
4625    ) {
4626        if scope.class_unit.is_none() || !scope.declarations_are_fields {
4627            return;
4628        }
4629        for member in stranded_declaration_run(node, self.source).declarations {
4630            self.add_macro_wrapped_declaration(member, scope, ancestry);
4631        }
4632    }
4633
4634    /// Index the constructor an access label swallowed in a reparsed class
4635    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
4636    ///
4637    /// The declarator is recovered by reparsing from that call to the end of
4638    /// the label's own statement, which is the same offset-preserving region
4639    /// reparse every other recovery here uses, so the recovered nodes carry
4640    /// their true source positions.
4641    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4642        let Some(class_unit) = scope.class_unit.clone() else {
4643            return;
4644        };
4645        if !scope.declarations_are_fields {
4646            return;
4647        }
4648        let class_name = class_unit.identifier().to_string();
4649        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
4650        else {
4651            return;
4652        };
4653        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
4654            return;
4655        };
4656        let root = tree.root_node();
4657        let Some(declarator) =
4658            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
4659        else {
4660            return;
4661        };
4662        let reparsed_ancestry = ParentIndex::new(root);
4663        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
4664        let range = cpp_declaration_range(definition.unwrap_or(declarator));
4665        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
4666        self.record_recovered_declarations(recovery, |visitor| {
4667            visitor.add_macro_wrapped_declaration(
4668                MacroWrappedDeclaration {
4669                    declarator,
4670                    range,
4671                    is_static: false,
4672                },
4673                scope,
4674                &reparsed_ancestry,
4675            );
4676        });
4677    }
4678
4679    fn add_macro_wrapped_declaration<'tree>(
4680        &mut self,
4681        declaration: MacroWrappedDeclaration<'tree>,
4682        scope: &ScopeInfo,
4683        ancestry: &ParentIndex<'tree>,
4684    ) {
4685        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
4686        else {
4687            return;
4688        };
4689        let code_unit =
4690            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4691        if self.parsed.contains_declaration(&code_unit) {
4692            self.parsed
4693                .record_navigation_range(code_unit, declaration.range);
4694            return;
4695        }
4696        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
4697        let signature = normalize_cpp_whitespace(
4698            self.source
4699                .get(declaration.range.start_byte..declaration.range.end_byte)
4700                .expect("a recovered declaration range covers one source range"),
4701        );
4702        let linkage = if declaration.is_static {
4703            CallableLinkage::Internal
4704        } else {
4705            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
4706        };
4707        // Whether this is a definition is a property of the recovered node, not
4708        // of the caller: a declarator that a `function_definition` gives a body
4709        // is a definition wherever the recovery found it.
4710        let declaration_only =
4711            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
4712        self.parsed.add_signature_with_metadata(
4713            code_unit.clone(),
4714            cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
4715                .with_declaration_only(declaration_only)
4716                .with_callable_linkage(linkage),
4717        );
4718        if let Some(parent) = &scope.class_unit {
4719            self.parsed.add_child(parent.clone(), code_unit);
4720        } else if let Some(module) = &scope.module {
4721            self.parsed.add_child(module.clone(), code_unit);
4722        }
4723    }
4724
4725    fn visit_error_swallowed_function_declaration<'tree>(
4726        &mut self,
4727        node: Node<'tree>,
4728        scope: &ScopeInfo,
4729    ) -> bool {
4730        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
4731            return false;
4732        };
4733        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
4734            return false;
4735        };
4736        let root = tree.root_node();
4737        let mut cursor = root.walk();
4738        let declarations = root
4739            .named_children(&mut cursor)
4740            .filter(|child| child.kind() != "comment")
4741            .collect::<Vec<_>>();
4742        let [declaration] = declarations.as_slice() else {
4743            return false;
4744        };
4745        if declaration.kind() != "declaration"
4746            || declaration.has_error()
4747            || declaration.start_byte() != start
4748            || declaration.end_byte() != end
4749        {
4750            return false;
4751        }
4752        let recovery = cpp_recovery_window(self.source, start, end);
4753        // The reparsed region is its own tree, so this walk indexes it itself.
4754        let reparsed_ancestry = ParentIndex::new(root);
4755        self.record_recovered_declarations(recovery, |visitor| {
4756            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
4757        });
4758        true
4759    }
4760
4761    /// Recover a K&R-compatibility prototype macro invocation such as
4762    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
4763    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
4764    /// defined only in headers outside this workspace (ruby's own
4765    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
4766    /// `name _((args))` and turns each such line into a malformed
4767    /// `declaration` or pointer-expression statement. Before this
4768    /// recovery, the ordinary declaration visitor read that wreckage as a
4769    /// field with the type and name swapped (e.g. a field literally named
4770    /// `VALUE`) and indexed no prototype for the real name at all.
4771    ///
4772    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
4773    /// preserve the declared name, a known one-argument compatibility macro,
4774    /// the nested argument list, and a live terminator. It rejects arbitrary
4775    /// identifiers and bare `ERROR` envelopes that flattened those relations;
4776    /// guessing there previously turned malformed non-macro declarations into
4777    /// invented functions. For each proven candidate this reparses the range
4778    /// before the macro token, the inner parenthesized argument node, and the
4779    /// terminating `;` as one included-range parse. That is the parser-native
4780    /// equivalent of expanding `#define _(args) args` while retaining original
4781    /// byte offsets. The result is admitted only when it is exactly one clean
4782    /// function prototype spanning the recovered run.
4783    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4784        for candidate in cpp_prototype_macro_candidates(node, self.source) {
4785            let start = candidate.run_start;
4786            let end = candidate.semicolon_end;
4787            if self.byte_range_is_inside_consumed_fragment(start, end) {
4788                continue;
4789            }
4790            let Some(tree) = parse_source_ranges_with_cancellation(
4791                &tree_sitter_cpp::LANGUAGE.into(),
4792                self.source,
4793                &candidate.ranges(),
4794                None,
4795            ) else {
4796                continue;
4797            };
4798            let root = tree.root_node();
4799            let mut cursor = root.walk();
4800            let declarations = root
4801                .named_children(&mut cursor)
4802                .filter(|child| child.kind() != "comment")
4803                .collect::<Vec<_>>();
4804            let [declaration] = declarations.as_slice() else {
4805                continue;
4806            };
4807            if declaration.kind() != "declaration"
4808                || declaration.has_error()
4809                || declaration.start_byte() != start
4810                || declaration.end_byte() != end
4811                || declaration
4812                    .child_by_field_name("declarator")
4813                    .and_then(extract_function_declarator)
4814                    .is_none()
4815            {
4816                continue;
4817            }
4818            let recovery = cpp_recovery_window(self.source, start, end);
4819            // The reparsed region is its own tree, so this walk indexes it
4820            // itself.
4821            let reparsed_ancestry = ParentIndex::new(root);
4822            self.record_recovered_declarations(recovery, |visitor| {
4823                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
4824            });
4825            self.consumed_fragment_regions.push((start, end));
4826        }
4827    }
4828
4829    /// Declare one Module per namespace level of `components` under
4830    /// `package_name`, and return the innermost level's package name and
4831    /// Module. A level an earlier definition already declared is reused.
4832    fn declare_namespace_levels(
4833        &mut self,
4834        mut package_name: String,
4835        components: Vec<String>,
4836        node: Node<'_>,
4837    ) -> (String, Option<CodeUnit>) {
4838        let mut module = None;
4839        for component in components {
4840            let full_name = if package_name.is_empty() {
4841                component
4842            } else {
4843                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
4844            };
4845            let level = CodeUnit::new_fq(
4846                self.file.clone(),
4847                CodeUnitType::Module,
4848                "",
4849                full_name.clone(),
4850                cpp_namespace_fq(&full_name),
4851            );
4852            if !self.parsed.contains_declaration(&level) {
4853                self.add_declaration(level.clone(), node, None, None);
4854            }
4855            package_name = full_name;
4856            module = Some(level);
4857        }
4858        (package_name, module)
4859    }
4860
4861    /// The scope for a node that C++ parse recovery displaced out of the
4862    /// namespaces enclosing it (issue #1537). When tree-sitter closes a
4863    /// damaged namespace early, the rest of its body and possibly the rest of
4864    /// the file lose the `namespace_definition` ancestors this walk reads a
4865    /// package from; the recovered region names the complete enclosing path.
4866    /// `None` when the walk's scope already names every enclosing namespace,
4867    /// or names a path another recovery established that this one does not
4868    /// extend.
4869    fn recovered_namespace_scope(
4870        &mut self,
4871        node: Node<'_>,
4872        scope: &ScopeInfo,
4873    ) -> Option<ScopeInfo> {
4874        let components = self
4875            .orphaned_namespaces
4876            .region_at(node.start_byte())?
4877            .components
4878            .clone();
4879        let package_name = components.join(CPP_PACKAGE_SEPARATOR);
4880        let extends = package_name.len() > scope.package_name.len()
4881            && package_name.starts_with(scope.package_name.as_str())
4882            && (scope.package_name.is_empty()
4883                || package_name[scope.package_name.len()..].starts_with(CPP_PACKAGE_SEPARATOR));
4884        if !extends {
4885            return None;
4886        }
4887        let (package_name, module) = self.declare_namespace_levels(String::new(), components, node);
4888        Some(ScopeInfo {
4889            package_name,
4890            module,
4891            // A recovered namespace body is no longer inside the class (or
4892            // function) that tree-sitter accidentally used as its wrapper.
4893            // Keep namespace/module context, but never carry that owner into
4894            // declarations published from the recovered region.
4895            class_unit: None,
4896            template_signature: None,
4897            template_metadata: None,
4898            declarations_are_fields: false,
4899            recovered_specialization_member_scope: false,
4900            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4901        })
4902    }
4903
4904    fn visit_namespace<'tree>(
4905        &mut self,
4906        node: Node<'tree>,
4907        scope: &ScopeInfo,
4908        stack: &mut Vec<CppWork<'tree>>,
4909        ancestry: &ParentIndex<'tree>,
4910    ) {
4911        let name_node = node.child_by_field_name("name");
4912        let Some(name_node) = name_node else {
4913            if let Some(body) = cpp_body_node(node) {
4914                stack.push(CppWork::Container(CppContainer {
4915                    node: body,
4916                    scope: scope.clone(),
4917                }));
4918            }
4919            return;
4920        };
4921        // Diagnostic corpora contain deliberately ill-formed global namespace
4922        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
4923        // the leading global `::` as the first anonymous child. Honor that AST
4924        // boundary instead of appending the name to the lexical namespace;
4925        // appending produced legacy names such as `outer::::outer::inner`, which
4926        // could not round-trip through the structured FqName boundary.
4927        let explicitly_global = name_node
4928            .child(0)
4929            .is_some_and(|child| !child.is_named() && child.kind() == "::");
4930        let components = cpp_namespace_name_components(name_node, self.source);
4931        if components.is_empty() {
4932            return;
4933        }
4934        // One Module per namespace level. C++17's `namespace a::b { ... }` is
4935        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
4936        // shorthand must declare `a` as well as `a::b` -- extracting only the
4937        // innermost level left the enclosing namespace undeclared and made the
4938        // two spellings of one construct disagree (issue #1878).
4939        let package_name = if explicitly_global {
4940            String::new()
4941        } else {
4942            scope.package_name.clone()
4943        };
4944        let (package_name, module) = self.declare_namespace_levels(package_name, components, node);
4945
4946        let namespace_scope = ScopeInfo {
4947            package_name,
4948            module,
4949            // C++ never nests a namespace inside a class, so a surviving
4950            // class_unit here is always recovery bleed: a malformed-region
4951            // boundary upstream mis-scoped this namespace block. Keeping the
4952            // owner would mint the namespace's declarations as class members
4953            // under a re-appended package, desyncing the fq boundary assert
4954            // (#2306). Dropping it is identity-neutral for valid code, where
4955            // class_unit is always empty at a namespace definition.
4956            class_unit: None,
4957            template_signature: scope.template_signature.clone(),
4958            template_metadata: scope.template_metadata.clone(),
4959            declarations_are_fields: false,
4960            recovered_specialization_member_scope: false,
4961            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4962        };
4963        let container = cpp_body_node(node).unwrap_or(node);
4964        // A malformed export-macro class body may turn the following class
4965        // into a descendant of a bogus function/labeled/error envelope. Those
4966        // descendants are not declaration containers and the ordinary walk
4967        // intentionally does not descend into them. Scan the namespace tree
4968        // once for the strict embedded class geometry before scheduling its
4969        // normal declarations. When one envelope matches, its helper recovers
4970        // every embedded class and the walk need not inspect its descendants.
4971        let mut candidates = vec![container];
4972        while let Some(candidate) = candidates.pop() {
4973            if matches!(
4974                candidate.kind(),
4975                "ERROR" | "function_definition" | "labeled_statement"
4976            ) && self.visit_embedded_function_like_export_classes(
4977                candidate,
4978                &namespace_scope,
4979                stack,
4980                ancestry,
4981            ) {
4982                continue;
4983            }
4984            for index in (0..candidate.named_child_count()).rev() {
4985                candidates.push(
4986                    candidate
4987                        .named_child(index)
4988                        .expect("index below the node's own named child count"),
4989                );
4990            }
4991        }
4992        stack.push(CppWork::Container(CppContainer {
4993            node: container,
4994            scope: namespace_scope,
4995        }));
4996    }
4997
4998    fn visit_class_like<'tree>(
4999        &mut self,
5000        node: Node<'tree>,
5001        scope: &ScopeInfo,
5002        stack: &mut Vec<CppWork<'tree>>,
5003        ancestry: &ParentIndex<'tree>,
5004    ) {
5005        let Some(name) = class_like_name(node, self.source, ancestry) else {
5006            return;
5007        };
5008        let name = qualified_class_name_chain(node, self.source, scope)
5009            .map(|chain| chain.join("$"))
5010            .unwrap_or(name);
5011        self.visit_named_class_like(node, name, scope, stack, ancestry);
5012    }
5013
5014    fn visit_named_class_like<'tree>(
5015        &mut self,
5016        node: Node<'tree>,
5017        name: String,
5018        scope: &ScopeInfo,
5019        stack: &mut Vec<CppWork<'tree>>,
5020        ancestry: &ParentIndex<'tree>,
5021    ) {
5022        let body = cpp_body_node(node);
5023        let definition_body_present = body.is_some();
5024        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5025            .then(|| extract_cpp_supertypes(node, self.source));
5026        self.visit_named_class_like_shape(
5027            node,
5028            name,
5029            body,
5030            definition_body_present,
5031            None,
5032            raw_supertypes,
5033            scope,
5034            stack,
5035            ancestry,
5036        );
5037    }
5038
5039    /// Whether this class-like declaration is a C tag that belongs to the
5040    /// enclosing non-aggregate scope rather than to the aggregate it is
5041    /// lexically written inside.
5042    ///
5043    /// `class_specifier` is deliberately excluded: `class` is not C, so text
5044    /// that spells one in a `.c` file is not C code and keeps the C++ reading
5045    /// rather than getting a half-C identity.
5046    fn mints_tag_at_enclosing_c_scope(
5047        &self,
5048        declaration_node: Node<'_>,
5049        scope: &ScopeInfo,
5050        ancestry: &ParentIndex<'_>,
5051    ) -> bool {
5052        self.c_tag_semantics
5053            && scope.class_unit.is_some()
5054            && class_like_name(declaration_node, self.source, ancestry).is_some()
5055            && matches!(
5056                declaration_node.kind(),
5057                "struct_specifier" | "union_specifier" | "enum_specifier"
5058            )
5059    }
5060
5061    #[allow(clippy::too_many_arguments)]
5062    fn visit_named_class_like_shape<'tree>(
5063        &mut self,
5064        declaration_node: Node<'tree>,
5065        name: String,
5066        body: Option<Node<'tree>>,
5067        definition_body_present: bool,
5068        explicit_range: Option<Range>,
5069        raw_supertypes: Option<Vec<String>>,
5070        scope: &ScopeInfo,
5071        stack: &mut Vec<CppWork<'tree>>,
5072        ancestry: &ParentIndex<'tree>,
5073    ) -> CodeUnit {
5074        let displaced_macro_tail = if explicit_range.is_none() {
5075            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5076        } else {
5077            None
5078        };
5079        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5080        let recovered_scope = self.scope_for_recovered_exported_class(
5081            declaration_node,
5082            &name,
5083            definition_body_present,
5084            scope,
5085            ancestry,
5086        );
5087        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
5088        // aggregate's member list is declared at the enclosing non-aggregate
5089        // scope, not nested inside the aggregate. `scope.class_unit` is the
5090        // only aggregate carrier in this walk, so dropping it puts the tag at
5091        // the nearest enclosing non-aggregate scope -- the module at file or
5092        // namespace scope, and the same block-scope representation a
5093        // function-local aggregate already gets. The tag's own body scope
5094        // below still owns its members, so fields and enumerators are
5095        // unaffected.
5096        let c_tag_scope;
5097        let scope =
5098            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5099                c_tag_scope = ScopeInfo {
5100                    class_unit: None,
5101                    ..recovered_scope.clone()
5102                };
5103                &c_tag_scope
5104            } else {
5105                &recovered_scope
5106            };
5107        let short_name = if let Some(parent) = &scope.class_unit {
5108            cpp_join_nested_short(parent.short_name(), &name)
5109        } else {
5110            name.clone()
5111        };
5112        // A top-level out-of-line qualified class definition (`struct
5113        // Outer::Inner { ... }` inside its namespace, #2246) carries its
5114        // nesting chain as the `$`-joined display name; push one Type/Nested
5115        // segment per class so segment-pop owner navigation keeps working.
5116        // Every other leaf name stays opaque so a literal `$` in a source
5117        // identifier never crosses the split/join boundary (#2140).
5118        let qualified_chain = if scope.class_unit.is_none() {
5119            qualified_class_name_chain(declaration_node, self.source, scope)
5120                .filter(|chain| chain.join("$") == name)
5121        } else {
5122            None
5123        };
5124        let fq = if let Some(chain) = qualified_chain {
5125            let mut fq = FqName::new();
5126            cpp_push_package(&mut fq, &scope.package_name);
5127            let mut first = true;
5128            for component in chain {
5129                let kind = if first {
5130                    SegmentKind::Type
5131                } else {
5132                    SegmentKind::Nested
5133                };
5134                fq.push(cpp_segment(&component, kind));
5135                first = false;
5136            }
5137            fq
5138        } else {
5139            cpp_leaf_fq(
5140                &scope.package_name,
5141                scope.class_unit.as_ref(),
5142                &name,
5143                SegmentKind::Nested,
5144                SegmentKind::Type,
5145            )
5146        };
5147        let code_unit = CodeUnit::with_signature_and_fq(
5148            self.file.clone(),
5149            CodeUnitType::Class,
5150            scope.package_name.clone(),
5151            short_name,
5152            scope.template_signature.clone(),
5153            false,
5154            fq,
5155        );
5156        let has_body = definition_body_present;
5157        if !has_body && self.parsed.contains_declaration(&code_unit) {
5158            self.parsed.record_navigation_range(
5159                code_unit.clone(),
5160                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5161            );
5162            return code_unit;
5163        }
5164        if has_body {
5165            if let Some(range) = explicit_range {
5166                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5167            } else {
5168                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5169            }
5170        } else {
5171            self.add_declaration(code_unit.clone(), declaration_node, None, None);
5172        }
5173        if let Some(raw_supertypes) = raw_supertypes {
5174            self.parsed
5175                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5176        }
5177        self.parsed.add_signature(
5178            code_unit.clone(),
5179            render_cpp_type_signature(
5180                declaration_node,
5181                self.source,
5182                scope.template_signature.as_deref(),
5183            ),
5184        );
5185        if let Some(metadata) = &scope.template_metadata {
5186            let primary_short_name = if let Some(parent) = &scope.class_unit {
5187                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5188            } else {
5189                metadata.primary_name.clone()
5190            };
5191            let primary_fq_name = CodeUnit::new(
5192                self.file.clone(),
5193                CodeUnitType::Class,
5194                scope.package_name.clone(),
5195                primary_short_name,
5196            )
5197            .fq_name();
5198            let mut metadata = metadata.clone();
5199            metadata.primary_fq_name = primary_fq_name;
5200            self.parsed
5201                .set_cpp_template_metadata(code_unit.clone(), metadata);
5202        }
5203        if let Some(parent) = &scope.class_unit {
5204            self.parsed.add_child(parent.clone(), code_unit.clone());
5205        } else if let Some(module) = &scope.module {
5206            self.parsed.add_child(module.clone(), code_unit.clone());
5207        }
5208
5209        if let Some(body) = body {
5210            let mut nested_scope = scope.clone();
5211            nested_scope.class_unit = Some(code_unit.clone());
5212            nested_scope.template_signature = scope.template_signature.clone();
5213            // Template metadata describes the class just created. It must not
5214            // leak into ordinary nested declarations in that class's body.
5215            // Recovered export-macro specializations carry a separate scope bit
5216            // for their declaration-shaped body members.
5217            nested_scope.template_metadata = None;
5218            // Export-macro class bodies recovered from a function_definition use
5219            // compound_statement children, whose direct fields are declarations.
5220            nested_scope.recovered_specialization_member_scope =
5221                scope.template_metadata.as_ref().is_some_and(|metadata| {
5222                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
5223                });
5224            nested_scope.declarations_are_fields =
5225                is_recovered_exported_class_container(declaration_node, self.source)
5226                    || nested_scope.recovered_specialization_member_scope;
5227            if let Some(displaced) = displaced_macro_tail {
5228                // A macro-shaped field without a source semicolon can make
5229                // tree-sitter consume the real class terminator as an ERROR
5230                // inside that field, then retain following namespace items as
5231                // later field-list children. Drain the proven class prefix
5232                // first and re-own only the structured tail with the outer
5233                // scope. The tail is pushed first because the work stack is
5234                // LIFO.
5235                push_cpp_sibling_range(
5236                    body,
5237                    displaced.split_index,
5238                    usize::MAX,
5239                    scope.clone(),
5240                    stack,
5241                );
5242                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5243            } else {
5244                stack.push(CppWork::Container(CppContainer {
5245                    node: body,
5246                    scope: nested_scope,
5247                }));
5248            }
5249        }
5250        if declaration_node.kind() == "enum_specifier" {
5251            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5252            if !self.has_enum_enumerator_units(&code_unit) {
5253                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5254            }
5255        }
5256        code_unit
5257    }
5258
5259    /// Whether the parse product already holds enumerator fields for `parent`,
5260    /// answered from the walk's carried-forward field ownership index.
5261    ///
5262    /// Built on the first enum's question and advanced by every declaration
5263    /// recorded after it, so a file that declares no enum -- most files -- pays
5264    /// nothing, and one that declares thousands pays a single pass instead of
5265    /// one per enum (#2786).
5266    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5267        if self.field_owners.is_none() {
5268            self.field_owners = Some(CppFieldOwnerIndex::of(
5269                self.parsed.declarations().iter(),
5270                self.file,
5271            ));
5272        }
5273        debug_assert_eq!(
5274            parent.source(),
5275            self.file,
5276            "the walk's declarations are declarations of the file it is walking"
5277        );
5278        let carried = self
5279            .field_owners
5280            .as_ref()
5281            .expect("the index was just ensured")
5282            .owns_fields(parent.package_name(), parent.short_name());
5283
5284        #[cfg(debug_assertions)]
5285        assert_eq!(
5286            carried,
5287            cpp_declarations_hold_owned_fields(
5288                self.parsed.declarations(),
5289                self.file,
5290                parent.package_name(),
5291                parent.short_name()
5292            ),
5293            "the carried-forward field index must answer what a fresh declaration scan \
5294             answers for {}",
5295            parent.fq_name()
5296        );
5297
5298        carried
5299    }
5300
5301    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5302        walk_named_tree_preorder(node, false, |child| {
5303            if child.kind() != "enumerator" {
5304                return WalkControl::Continue;
5305            }
5306            let Some(name_node) = child.child_by_field_name("name") else {
5307                return WalkControl::Continue;
5308            };
5309            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5310            if name.is_empty() {
5311                return WalkControl::Continue;
5312            }
5313            let code_unit = CodeUnit::new_fq(
5314                self.file.clone(),
5315                CodeUnitType::Field,
5316                scope.package_name.clone(),
5317                cpp_join_member_short(parent.short_name(), &name),
5318                parent
5319                    .fq()
5320                    .clone()
5321                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5322            );
5323            if self.parsed.contains_declaration(&code_unit) {
5324                return WalkControl::Continue;
5325            }
5326            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5327            self.parsed.add_signature(
5328                code_unit,
5329                normalize_cpp_whitespace(node_text(child, self.source)),
5330            );
5331            WalkControl::Continue
5332        });
5333    }
5334
5335    fn visit_enum_enumerators_from_text(
5336        &mut self,
5337        node: Node<'_>,
5338        scope: &ScopeInfo,
5339        parent: &CodeUnit,
5340    ) {
5341        let text = node_text(node, self.source);
5342        let Some((_, body)) = text.split_once('{') else {
5343            return;
5344        };
5345        let Some((body, _)) = body.rsplit_once('}') else {
5346            return;
5347        };
5348        for entry in body.split(',') {
5349            let trimmed = entry.trim();
5350            let name = trimmed
5351                .split('=')
5352                .next()
5353                .unwrap_or("")
5354                .split_whitespace()
5355                .next()
5356                .unwrap_or("");
5357            if name.is_empty() {
5358                continue;
5359            }
5360            let code_unit = CodeUnit::new_fq(
5361                self.file.clone(),
5362                CodeUnitType::Field,
5363                scope.package_name.clone(),
5364                cpp_join_member_short(parent.short_name(), name),
5365                parent
5366                    .fq()
5367                    .clone()
5368                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
5369            );
5370            if self.parsed.contains_declaration(&code_unit) {
5371                continue;
5372            }
5373            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5374            self.parsed.add_signature(code_unit, trimmed.to_string());
5375        }
5376    }
5377
5378    fn visit_function_definition<'tree>(
5379        &mut self,
5380        node: Node<'tree>,
5381        scope: &ScopeInfo,
5382        stack: &mut Vec<CppWork<'tree>>,
5383        ancestry: &ParentIndex<'tree>,
5384    ) {
5385        // An attribute-like macro invocation whose argument is a declaration can
5386        // swallow every declaration written after it into one bogus
5387        // `function_definition` (#2551). This owns the whole region, so it runs
5388        // ahead of `visit_macro_swallowed_function_declarations` below, which
5389        // admits the same envelope by its head macro token and reads single
5390        // declarators out of nodes this recovery reparses properly.
5391        if self.visit_collapsed_macro_declaration_run(node, scope) {
5392            return;
5393        }
5394        // A file-scope object-like macro sentinel the parser cannot see (issue
5395        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
5396        // prefixes as a bogus `function_definition` that swallows real namespaces,
5397        // classes, and members. Reparse the swallowed interior as C++ items so the
5398        // ordinary declaration visitors index it with byte/line-exact ownership.
5399        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5400            return;
5401        }
5402        if node.has_error() {
5403            self.visit_macro_swallowed_function_declarations(node, scope);
5404        }
5405        if let Some((class_node, name, raw_supertypes)) =
5406            recover_exported_class_function_definition(node, self.source)
5407        {
5408            let body = cpp_body_node(class_node);
5409            let displaced_namespace = cpp_body_node(node)
5410                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
5411            let fragmented = cpp_body_node(node).and_then(|body| {
5412                fragmented_export_function_body_region(
5413                    node,
5414                    body,
5415                    self.source,
5416                    displaced_namespace.as_ref(),
5417                )
5418            });
5419            // The recovery tuple's first node is the class-like type when the
5420            // parser exposes one, but the synthetic wrapper owns the compound
5421            // statement that contains the truncated class body. Use the
5422            // wrapper body for fragmented-member detection; retain the
5423            // class-node body for the ordinary (non-fragmented) path below.
5424            if let Some(fragmented) = fragmented {
5425                // The lifted sibling no longer sits below the parser-visible
5426                // namespace node. Restore the current parent scope when the
5427                // ordinary work walk reaches that class.
5428                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
5429                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
5430                {
5431                    let mut boundary_scope = scope.clone();
5432                    for sibling in cpp_following_named_siblings(node, self.source) {
5433                        if sibling.start_byte() >= boundary.start_byte() {
5434                            break;
5435                        }
5436                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
5437                            boundary_scope.visible_using_namespaces.push(namespace);
5438                        }
5439                    }
5440                    self.recovered_class_sibling_scopes
5441                        .insert(boundary.id(), boundary_scope);
5442                }
5443                let mut recovered_constructor = None;
5444                let mut recovered_prefix_tree = None;
5445                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
5446                {
5447                    Some(FragmentedExportMembers::Complete(tree)) => {
5448                        if let Some(body) = body
5449                            && let Some(range) =
5450                                cpp_reparsed_synthetic_initializer_constructor_range(
5451                                    tree.root_node(),
5452                                    &name,
5453                                    self.source,
5454                                    body.end_byte(),
5455                                )
5456                        {
5457                            recovered_constructor = Some(range);
5458                            recovered_prefix_tree = Some(tree);
5459                            None
5460                        } else {
5461                            Some(FragmentedExportMembers::Complete(tree))
5462                        }
5463                    }
5464                    outcome => outcome,
5465                };
5466                let mut class_stack = Vec::new();
5467                let class_unit = self.visit_named_class_like_shape(
5468                    class_node,
5469                    name,
5470                    None,
5471                    true,
5472                    Some(fragmented.class_range),
5473                    raw_supertypes,
5474                    scope,
5475                    &mut class_stack,
5476                    ancestry,
5477                );
5478                self.parsed
5479                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
5480                        recovery: fragmented.class_range,
5481                        unit: class_unit.clone(),
5482                    });
5483                let complete = outcome.is_some_and(|outcome| {
5484                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
5485                });
5486                if complete {
5487                    self.consumed_fragment_regions
5488                        .push((node.start_byte(), fragmented.class_range.end_byte));
5489                } else {
5490                    // The reparse can fail when the first constructor or a
5491                    // method body is split into statement-shaped siblings.
5492                    // Keep the recovered class envelope, but do not visit the
5493                    // synthetic wrapper body: its initializer expressions can
5494                    // look like same-named member functions (for example
5495                    // `Token.location(loc)`). Re-own only the original sibling
5496                    // nodes that fall inside the proven class range. Their CST
5497                    // shapes retain the real field/function kinds and ranges.
5498                    let member_scope = ScopeInfo {
5499                        package_name: class_unit.package_name().to_string(),
5500                        module: scope.module.clone(),
5501                        class_unit: Some(class_unit.clone()),
5502                        template_signature: scope.template_signature.clone(),
5503                        template_metadata: None,
5504                        declarations_are_fields: true,
5505                        recovered_specialization_member_scope: false,
5506                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
5507                    };
5508                    for candidate in cpp_following_named_siblings(node, self.source) {
5509                        if candidate.start_byte() >= fragmented.reparse_end {
5510                            break;
5511                        }
5512                        if cpp_fragment_sibling_is_class_member(
5513                            candidate,
5514                            fragmented.reparse_end,
5515                            self.source,
5516                        ) {
5517                            self.recovered_class_sibling_scopes
5518                                .insert(candidate.id(), member_scope.clone());
5519                        }
5520                    }
5521                    if let Some(range) = recovered_constructor
5522                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
5523                    {
5524                        self.visit_recovered_fragment_prefix_members(
5525                            prefix_tree.root_node(),
5526                            range.start,
5527                            &class_unit,
5528                            scope,
5529                            ancestry,
5530                        );
5531                        self.visit_recovered_fragment_constructor(
5532                            range,
5533                            body,
5534                            class_node,
5535                            &class_unit,
5536                            scope,
5537                            ancestry,
5538                        );
5539                    }
5540                }
5541                if let Some(boundary) = displaced_namespace {
5542                    for item in boundary.namespace_items {
5543                        self.recovered_class_sibling_scopes
5544                            .insert(item.id(), scope.clone());
5545                    }
5546                }
5547                stack.extend(class_stack);
5548                return;
5549            }
5550            let mut stack = Vec::new();
5551            let class_unit = self.visit_named_class_like_shape(
5552                class_node,
5553                name,
5554                body,
5555                body.is_some(),
5556                None,
5557                raw_supertypes,
5558                scope,
5559                &mut stack,
5560                ancestry,
5561            );
5562            self.parsed
5563                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5564                    recovery: cpp_declaration_range(node),
5565                    unit: class_unit,
5566                });
5567            // Issue #1524: the bogus `function_definition` body can run past
5568            // the class's true closing brace (the parse ends it with a
5569            // zero-width `MISSING "}"`), swallowing following namespace-scope
5570            // siblings -- they would index as members of the recovered class.
5571            // When the body's text-balanced close lands before the body's own
5572            // end, re-own the swallowed tail with the outer scope instead.
5573            if let Some(body) = body
5574                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
5575                && class_close < body.end_byte()
5576            {
5577                let split = {
5578                    let mut cursor = body.walk();
5579                    body.named_children(&mut cursor)
5580                        .position(|child| child.start_byte() > class_close)
5581                };
5582                if let Some(split) = split {
5583                    // The seeded work is a single Container over the whole
5584                    // body with the class scope; replace it with the bounded
5585                    // head (class scope) plus the swallowed tail (outer
5586                    // scope). Push tail first so the head drains first.
5587                    let seeded = stack.pop();
5588                    match seeded {
5589                        Some(CppWork::Container(container)) => {
5590                            push_cpp_sibling_range(
5591                                body,
5592                                split,
5593                                usize::MAX,
5594                                scope.clone(),
5595                                &mut stack,
5596                            );
5597                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
5598                        }
5599                        // visit_named_class_like_shape always seeds exactly
5600                        // one Container when a body is present.
5601                        _ => unreachable!("exported-class seed is always one Container"),
5602                    }
5603                }
5604            }
5605            while let Some(work) = stack.pop() {
5606                match work {
5607                    CppWork::Container(container) => {
5608                        push_cpp_container_work(container.node, container.scope, &mut stack);
5609                    }
5610                    CppWork::Siblings(siblings) => {
5611                        advance_cpp_siblings(siblings, self.source, &mut stack);
5612                    }
5613                    CppWork::Node(work) => {
5614                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
5615                    }
5616                }
5617            }
5618            return;
5619        }
5620        let recovered_constraint_constructor =
5621            cpp_recovered_template_macro_constructor(node, self.source);
5622        let declarator = recovered_constraint_constructor
5623            .map(|(declarator, _)| declarator)
5624            .or_else(|| node.child_by_field_name("declarator"));
5625        let Some(declarator) = declarator else {
5626            self.visit_malformed_function_definition_container(node, scope, stack);
5627            return;
5628        };
5629        let Some(function_declarator) = extract_function_declarator(declarator) else {
5630            self.visit_malformed_function_definition_container(node, scope, stack);
5631            return;
5632        };
5633        let function = if let Some((_, callable_name)) =
5634            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
5635        {
5636            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
5637        } else {
5638            extract_function_info(function_declarator, self.source, scope)
5639        };
5640        let Some(mut function) = function else {
5641            self.visit_malformed_function_definition_container(node, scope, stack);
5642            return;
5643        };
5644        if let Some((_, template_parameter)) = recovered_constraint_constructor {
5645            function.signature = format!(
5646                "template <{}>{}",
5647                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
5648                function.signature
5649            );
5650        }
5651        let code_unit = function.code_unit(self.file.clone());
5652        // Keep an earlier same-file prototype as another physical occurrence
5653        // of this callable. `CodeUnit` already identifies the role-neutral
5654        // overload, while ranges and signature metadata describe its
5655        // declaration/definition occurrences.
5656        self.add_declaration(code_unit.clone(), node, None, None);
5657        let signature = if recovered_constraint_constructor.is_some() {
5658            normalize_cpp_whitespace(node_text(function_declarator, self.source))
5659        } else {
5660            render_cpp_function_display_signature_from_node(
5661                node,
5662                self.source,
5663                scope.template_signature.as_deref(),
5664                true,
5665                ancestry,
5666            )
5667        };
5668        self.parsed.add_signature_with_metadata(
5669            code_unit.clone(),
5670            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
5671                .with_declaration_only(false)
5672                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
5673        );
5674        if let Some(parent) = &scope.class_unit {
5675            self.parsed.add_child(parent.clone(), code_unit);
5676        } else if let Some(module) = &scope.module {
5677            self.parsed.add_child(module.clone(), code_unit);
5678        }
5679    }
5680
5681    /// Recover the namespace lost when tree-sitter promotes an export-macro
5682    /// class definition to a root-level `function_definition`.  Only a
5683    /// body-bearing, top-level recovery may borrow a namespace, and only when
5684    /// one earlier namespace-scope forward declaration proves the identity.
5685    fn scope_for_recovered_exported_class<'tree>(
5686        &mut self,
5687        node: Node<'tree>,
5688        name: &str,
5689        definition_body_present: bool,
5690        scope: &ScopeInfo,
5691        ancestry: &ParentIndex<'tree>,
5692    ) -> ScopeInfo {
5693        if !definition_body_present
5694            || !scope.package_name.is_empty()
5695            || scope.class_unit.is_some()
5696            || !(is_recovered_exported_class_container(node, self.source)
5697                || recover_function_like_export_class_pair(node, self.source).is_some()
5698                || recover_embedded_function_like_export_classes(node, self.source)
5699                    .iter()
5700                    .any(|recovered| recovered.name == name)
5701                || matches!(node.kind(), "declaration" | "field_declaration")
5702                    && recover_exported_class_declaration(node, self.source).is_some()
5703                || matches!(
5704                    node.kind(),
5705                    "class_specifier" | "struct_specifier" | "union_specifier"
5706                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
5707                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
5708                        name_node,
5709                        self.source,
5710                    )))
5711                }) || ancestry.parent(node).is_some_and(|parent| {
5712                    matches!(parent.kind(), "declaration" | "field_declaration")
5713                        && recover_exported_class_declaration(parent, self.source).is_some()
5714                        || is_recovered_exported_class_container(parent, self.source)
5715                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
5716        {
5717            return scope.clone();
5718        }
5719        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
5720        let Some(package_name) = borrowed_namespace
5721            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
5722        else {
5723            return scope.clone();
5724        };
5725
5726        let module = CodeUnit::new_fq(
5727            self.file.clone(),
5728            CodeUnitType::Module,
5729            "",
5730            package_name.clone(),
5731            cpp_namespace_fq(&package_name),
5732        );
5733        let mut recovered = scope.clone();
5734        recovered.package_name = package_name;
5735        recovered.module = Some(module);
5736        recovered
5737    }
5738
5739    /// The unique namespace-scope forward declaration of `name` that precedes
5740    /// `recovered_node`, answered from the walk's carried-forward scan of the
5741    /// tree `recovered_node` belongs to.
5742    ///
5743    /// The scan is built on the first question and advanced by each later one,
5744    /// so a file that never reaches this path -- almost every file -- pays
5745    /// nothing, and one that reaches it thousands of times pays a single pass
5746    /// (#2754).
5747    fn unique_earlier_namespace_forward<'tree>(
5748        &mut self,
5749        recovered_node: Node<'tree>,
5750        name: &str,
5751        ancestry: &ParentIndex<'tree>,
5752    ) -> Option<String> {
5753        let mut root = recovered_node;
5754        while let Some(parent) = ancestry.parent(root) {
5755            root = parent;
5756        }
5757        let source = self.source;
5758        let scan = self
5759            .namespace_forward_scans
5760            .entry(CppTreeIdentity::of(root))
5761            .or_default();
5762        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
5763        let borrowed = scan.unique_earlier_forward(name, recovered_node);
5764
5765        #[cfg(debug_assertions)]
5766        assert_eq!(
5767            borrowed,
5768            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
5769            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
5770             for {name} at byte {}",
5771            recovered_node.start_byte()
5772        );
5773
5774        borrowed
5775    }
5776
5777    fn visit_malformed_function_definition_container<'tree>(
5778        &mut self,
5779        node: Node<'tree>,
5780        scope: &ScopeInfo,
5781        stack: &mut Vec<CppWork<'tree>>,
5782    ) {
5783        let Some(body) = cpp_body_node(node) else {
5784            return;
5785        };
5786        if !cpp_contains_namespace_definition(body) {
5787            return;
5788        }
5789        stack.push(CppWork::Container(CppContainer {
5790            node: body,
5791            scope: scope.clone(),
5792        }));
5793    }
5794
5795    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
5796    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
5797    /// emits for a sentinel-prefixed region, reparse the interior after the
5798    /// sentinel identifier as real C++ items -- confined to the region so
5799    /// every reparsed node keeps its original byte/line position -- and run the
5800    /// ordinary container visitation over the result. Returns `true` when it fired
5801    /// (the caller must then skip normal function processing). Nested sentinel
5802    /// regions recover recursively: the reparsed interior is walked through the
5803    /// same `visit_function_definition` path, so a sentinel inside the region hits
5804    /// this recovery again.
5805    /// Runs `reparse_walk` and records every declaration it mints as a
5806    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
5807    /// `recovery` (issue #1657). A reparsed sentinel region has no single
5808    /// recovered envelope unit: the ordinary visitors mint namespaces,
5809    /// classes, and members directly from the reparsed tree, so the walk's
5810    /// declaration delta is the recovered set. Records are ordered by
5811    /// declaration start byte so the parse product stays deterministic.
5812    fn record_recovered_declarations(
5813        &mut self,
5814        recovery: Range,
5815        reparse_walk: impl FnOnce(&mut Self),
5816    ) {
5817        // The set difference this used to be, kept as the oracle every answer
5818        // is asserted against (#2787).
5819        #[cfg(any(debug_assertions, test))]
5820        let before = self.parsed.declarations().clone();
5821
5822        self.recovery_captures.push(CppRecoveryCapture::default());
5823        reparse_walk(self);
5824        let captured = self
5825            .recovery_captures
5826            .pop()
5827            .expect("the capture this call pushed is the one it pops");
5828
5829        // The capture holds every declaration created while it was open, once
5830        // each and in creation order, so the recovered set costs what the
5831        // recovery made rather than everything the file has declared so far.
5832        // One filter is left to apply: a created declaration that a later
5833        // deferred replacement removed is not in the parse product to report.
5834        let mut minted: Vec<CodeUnit> = captured
5835            .created
5836            .into_iter()
5837            .filter(|unit| self.parsed.contains_declaration(unit))
5838            .collect();
5839        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5840
5841        #[cfg(any(debug_assertions, test))]
5842        {
5843            let mut rediscovered: Vec<CodeUnit> = self
5844                .parsed
5845                .declarations()
5846                .iter()
5847                .filter(|unit| !before.contains(*unit))
5848                .cloned()
5849                .collect();
5850            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
5851            assert_eq!(
5852                minted, rediscovered,
5853                "the captured recovered set must be the declaration delta of the reparse \
5854                 walk over {recovery:?}"
5855            );
5856        }
5857
5858        for unit in minted {
5859            self.parsed
5860                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5861                    recovery,
5862                    unit,
5863                });
5864        }
5865    }
5866
5867    /// Where one recovered declaration sorts: by start byte, then by name, so
5868    /// the parse product stays deterministic.
5869    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
5870        let start = self
5871            .parsed
5872            .declaration_ranges(unit)
5873            .first()
5874            .map(|range| range.start_byte)
5875            .unwrap_or(usize::MAX);
5876        (start, unit.fq_name().to_string())
5877    }
5878
5879    fn visit_sentinel_macro_region<'tree>(
5880        &mut self,
5881        node: Node<'tree>,
5882        scope: &ScopeInfo,
5883        stack: &mut Vec<CppWork<'tree>>,
5884        ancestry: &ParentIndex<'tree>,
5885    ) -> bool {
5886        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
5887            return true;
5888        }
5889        if let Some((
5890            reparse_start,
5891            class_start,
5892            body_start,
5893            class_close_start,
5894            class_close_end,
5895            class_close_line,
5896        )) = cpp_sentinel_macro_class_region(node, self.source)
5897        {
5898            let Some(class_tree) =
5899                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
5900            else {
5901                return false;
5902            };
5903            let class_root = class_tree.root_node();
5904            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
5905            // A region reparse is its own tree and needs its own parent index.
5906            let class_ancestry = ParentIndex::new(class_root);
5907            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
5908                class_root,
5909                template_node,
5910                self.source,
5911                &class_ancestry,
5912            ) else {
5913                return false;
5914            };
5915            let class_node = reparsed_class.declaration_node;
5916            let name = reparsed_class.name;
5917            let mut class_scope = scope.clone();
5918            if let Some(template_node) = template_node {
5919                class_scope.template_signature =
5920                    cpp_template_signature(template_node, class_node, self.source);
5921                class_scope.template_metadata =
5922                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
5923            }
5924            let Some(body_tree) =
5925                cpp_reparse_region_items(self.source, body_start, class_close_start)
5926            else {
5927                return false;
5928            };
5929            let raw_supertypes = reparsed_class.raw_supertypes;
5930            let class_range = Range {
5931                start_byte: class_start,
5932                end_byte: class_close_end,
5933                start_line: class_node.start_position().row + 1,
5934                end_line: class_close_line,
5935            };
5936            let class_scope = self.scope_for_recovered_exported_class(
5937                class_node,
5938                &name,
5939                true,
5940                &class_scope,
5941                ancestry,
5942            );
5943            let mut class_stack = Vec::new();
5944            let class_unit = self.visit_named_class_like_shape(
5945                class_node,
5946                name,
5947                None,
5948                true,
5949                Some(class_range),
5950                raw_supertypes,
5951                &class_scope,
5952                &mut class_stack,
5953                ancestry,
5954            );
5955            self.parsed
5956                .record_materialization(MaterializationRecord::RecoveredDeclaration {
5957                    recovery: class_range,
5958                    unit: class_unit.clone(),
5959                });
5960            let member_scope = ScopeInfo {
5961                package_name: class_scope.package_name.clone(),
5962                module: class_scope.module.clone(),
5963                class_unit: Some(class_unit),
5964                template_signature: class_scope.template_signature.clone(),
5965                template_metadata: None,
5966                declarations_are_fields: true,
5967                recovered_specialization_member_scope: false,
5968                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
5969            };
5970            // The padded body reparse is its own tree, so it indexes itself.
5971            let body_root = body_tree.root_node();
5972            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
5973            // Register only after the padded body reparse: its nodes deliberately
5974            // retain offsets inside the consumed region and must be visited first.
5975            self.consumed_fragment_regions
5976                .push((node.start_byte(), class_close_end));
5977            // An ERROR envelope can hold real sibling declarations after the
5978            // recovered class's close (the suffix-reparse boundary in
5979            // `cpp_sentinel_macro_class_region` partitions, it does not
5980            // consume). Walk the envelope's remaining children normally; the
5981            // consumed region above keeps the recovered class from being
5982            // indexed twice.
5983            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
5984                stack.push(CppWork::Container(CppContainer {
5985                    node,
5986                    scope: scope.clone(),
5987                }));
5988            }
5989            return true;
5990        }
5991        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
5992            return false;
5993        };
5994        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5995            return false;
5996        };
5997        let root = tree.root_node();
5998        if !cpp_reparsed_items_are_indexable(root, self.source) {
5999            return false;
6000        }
6001        let recovery = cpp_recovery_window(self.source, start, end);
6002        // The reparsed region is its own tree, so this walk indexes it itself.
6003        let reparsed_ancestry = ParentIndex::new(root);
6004        self.record_recovered_declarations(recovery, |visitor| {
6005            visitor.visit_container(
6006                root,
6007                &reparsed_ancestry,
6008                &scope.package_name,
6009                scope.module.clone(),
6010                scope.class_unit.clone(),
6011                scope.template_signature.clone(),
6012                scope.visible_using_namespaces.clone(),
6013            );
6014        });
6015        if end > node.end_byte() {
6016            self.consumed_fragment_regions
6017                .push((node.start_byte(), end));
6018        } else if node.kind() == "ERROR" && node.end_byte() > end {
6019            // The sentinel region ended at the first recovered class-like item
6020            // but the ERROR envelope keeps real sibling declarations after it
6021            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
6022            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
6023            // envelope's remaining children normally; the consumed region
6024            // keeps the reparsed prefix from being indexed twice.
6025            self.consumed_fragment_regions
6026                .push((node.start_byte(), end));
6027            stack.push(CppWork::Container(CppContainer {
6028                node,
6029                scope: scope.clone(),
6030            }));
6031        }
6032        true
6033    }
6034
6035    /// Re-own complete class declarations from the structured Abseil
6036    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
6037    /// its direct CST children already prove both namespace components and the
6038    /// class bodies, so the ordinary class/member visitor can retain ownership
6039    /// and exact source ranges without admitting unrelated callable bodies.
6040    fn visit_nested_namespace_sentinel<'tree>(
6041        &mut self,
6042        node: Node<'tree>,
6043        scope: &ScopeInfo,
6044        ancestry: &ParentIndex<'tree>,
6045    ) -> bool {
6046        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6047            return false;
6048        };
6049
6050        let mut package_name = scope.package_name.clone();
6051        let mut module = scope.module.clone();
6052        for component in recovered.namespace_components {
6053            package_name = if package_name.is_empty() {
6054                component
6055            } else {
6056                format!("{package_name}::{component}")
6057            };
6058            let namespace_module = CodeUnit::new_fq(
6059                self.file.clone(),
6060                CodeUnitType::Module,
6061                "",
6062                package_name.clone(),
6063                cpp_namespace_fq(&package_name),
6064            );
6065            if !self.parsed.contains_declaration(&namespace_module) {
6066                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6067            }
6068            module = Some(namespace_module);
6069        }
6070
6071        let recovered_scope = ScopeInfo {
6072            package_name,
6073            module,
6074            class_unit: scope.class_unit.clone(),
6075            template_signature: scope.template_signature.clone(),
6076            template_metadata: scope.template_metadata.clone(),
6077            declarations_are_fields: false,
6078            recovered_specialization_member_scope: false,
6079            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6080        };
6081        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6082            recovered.function,
6083            recovered.body,
6084            self.source,
6085            ancestry,
6086        ) {
6087            let mut class_scope = recovered_scope.clone();
6088            if let Some(template_node) = fragmented.template_node {
6089                class_scope.template_signature =
6090                    cpp_template_signature(template_node, fragmented.class_node, self.source);
6091                class_scope.template_metadata = cpp_template_metadata(
6092                    template_node,
6093                    fragmented.class_node,
6094                    self.source,
6095                    ancestry,
6096                );
6097            }
6098            if let Some(outcome) = self
6099                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6100            {
6101                let mut class_stack = Vec::new();
6102                let class_unit = self.visit_named_class_like_shape(
6103                    fragmented.class_node,
6104                    fragmented.name.clone(),
6105                    None,
6106                    true,
6107                    Some(fragmented.fragmented.class_range),
6108                    fragmented.raw_supertypes.clone(),
6109                    &class_scope,
6110                    &mut class_stack,
6111                    ancestry,
6112                );
6113                self.parsed
6114                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6115                        recovery: fragmented.fragmented.class_range,
6116                        unit: class_unit.clone(),
6117                    });
6118                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6119                    self.consumed_fragment_regions.push((
6120                        fragmented.consumed_start,
6121                        fragmented.fragmented.class_range.end_byte,
6122                    ));
6123                }
6124            }
6125        }
6126        // The class requirement above is the admission gate; once admitted,
6127        // traverse the whole proven inner namespace body so sibling aliases,
6128        // functions, and variables are not silently discarded. The body is a
6129        // node of the tree being walked, so it reuses that tree's index.
6130        self.run_container_work(recovered.body, recovered_scope, ancestry);
6131        true
6132    }
6133
6134    fn visit_declaration<'tree>(
6135        &mut self,
6136        node: Node<'tree>,
6137        scope: &ScopeInfo,
6138        in_class_body: bool,
6139        stack: &mut Vec<CppWork<'tree>>,
6140        ancestry: &ParentIndex<'tree>,
6141    ) {
6142        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6143            return;
6144        }
6145        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6146            return;
6147        }
6148        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6149            !cpp_active_template_type_parameter(
6150                node,
6151                node_text(declarator, self.source),
6152                self.source,
6153                ancestry,
6154            )
6155        }) {
6156            return;
6157        }
6158        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6159            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
6160            // the following `Imaging image` member into one malformed field.
6161            // The ERROR's identifier is the actual declarator; route it
6162            // through the ordinary field path so its parent, range, and
6163            // signature metadata remain consistent with every other member.
6164            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6165            return;
6166        }
6167        if in_class_body
6168            && let Some(parent) = scope.class_unit.as_ref()
6169            && let Some(call) =
6170                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6171        {
6172            self.visit_recovered_macro_qualified_constructor_definition(
6173                node, call, scope, ancestry,
6174            );
6175            return;
6176        }
6177        if in_class_body
6178            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6179        {
6180            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6181            return;
6182        }
6183        if in_class_body
6184            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6185        {
6186            for member in members {
6187                self.add_macro_wrapped_declaration(member, scope, ancestry);
6188            }
6189            return;
6190        }
6191        if in_class_body
6192            && let Some(declarators) =
6193                recovered_macro_qualified_field_declarators(node, self.source)
6194        {
6195            for declarator in declarators {
6196                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6197            }
6198            return;
6199        }
6200        let recovered_alias_names = recovered_type_alias_names(node, self.source);
6201        if !recovered_alias_names.is_empty() {
6202            self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6203            return;
6204        }
6205        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
6206        {
6207            return;
6208        }
6209        if self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6210            return;
6211        }
6212
6213        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6214            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6215                // Issue #938: the members tree-sitter scattered out of the fragmented
6216                // multiple-base export node are reparsed from their true body region
6217                // and re-owned as members of the recovered class, with an explicit
6218                // navigation range spanning to the displaced closing brace.
6219                if let Some(outcome) =
6220                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6221                {
6222                    let consumed_region = (
6223                        recovered.declaration_node.end_byte(),
6224                        fragmented.class_range.end_byte,
6225                    );
6226                    let code_unit = self.visit_named_class_like_shape(
6227                        recovered.declaration_node,
6228                        recovered.name,
6229                        None,
6230                        true,
6231                        Some(fragmented.class_range),
6232                        recovered.raw_supertypes,
6233                        scope,
6234                        stack,
6235                        ancestry,
6236                    );
6237                    self.parsed.record_materialization(
6238                        MaterializationRecord::RecoveredDeclaration {
6239                            recovery: fragmented.class_range,
6240                            unit: code_unit.clone(),
6241                        },
6242                    );
6243                    let consume_fragment =
6244                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6245                    // Everything between the fragmented declaration and its displaced
6246                    // closing brace now belongs to the recovered class; keep the
6247                    // ordinary walk from re-indexing those scattered siblings at top
6248                    // level. Register the consumed region only after indexing because
6249                    // the reparsed nodes retain byte offsets inside that same region.
6250                    if consume_fragment {
6251                        self.consumed_fragment_regions.push(consumed_region);
6252                    }
6253                    return;
6254                }
6255            }
6256            let uses_initializer_body = recovered.uses_initializer_body;
6257            let definition_body_present = recovered.body.is_some();
6258            let class_unit = self.visit_named_class_like_shape(
6259                recovered.declaration_node,
6260                recovered.name,
6261                recovered.body,
6262                definition_body_present,
6263                None,
6264                recovered.raw_supertypes,
6265                scope,
6266                stack,
6267                ancestry,
6268            );
6269            self.parsed
6270                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6271                    recovery: cpp_declaration_range(node),
6272                    unit: class_unit,
6273                });
6274            if uses_initializer_body {
6275                return;
6276            }
6277        }
6278
6279        let mut handled_function = false;
6280        let mut handled_declarator = false;
6281        let mut cursor = node.walk();
6282        for child in node.named_children(&mut cursor) {
6283            if matches!(
6284                child.kind(),
6285                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6286            ) {
6287                // A named class-like definition remains a declaration even when
6288                // the same statement also declares an object, for example
6289                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
6290                // declaration's type and `kind` as its declarator.  Dropping the
6291                // type here loses both its nested owner and every later lexical
6292                // reference to it.  A body is the structured proof that this is
6293                // a definition rather than an elaborated type use such as
6294                // `class Kind value;`.
6295                if cpp_body_node(child).is_some() {
6296                    self.visit_class_like(child, scope, stack, ancestry);
6297                }
6298                continue;
6299            }
6300        }
6301
6302        let mut cursor = node.walk();
6303        for child in node.children_by_field_name("declarator", &mut cursor) {
6304            if crate::structural::is_recovered_designator_init_declarator(child) {
6305                handled_declarator = true;
6306                continue;
6307            }
6308            if in_class_body
6309                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6310            {
6311                handled_declarator = true;
6312                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6313                continue;
6314            }
6315            if let Some(kind) = classify_declarator(child) {
6316                handled_declarator = true;
6317                match kind {
6318                    DeclaratorKind::Function(function_declarator) => {
6319                        handled_function = true;
6320                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6321                    }
6322                    DeclaratorKind::Variable(variable_declarator) => {
6323                        self.visit_variable_declaration(
6324                            node,
6325                            variable_declarator,
6326                            scope,
6327                            in_class_body,
6328                            ancestry,
6329                        );
6330                    }
6331                }
6332            }
6333        }
6334
6335        if !handled_declarator {
6336            let mut cursor = node.walk();
6337            for child in node.named_children(&mut cursor) {
6338                if crate::structural::is_recovered_designator_init_declarator(child) {
6339                    handled_declarator = true;
6340                    continue;
6341                }
6342                if !is_unfielded_declarator_candidate(child) {
6343                    continue;
6344                }
6345                let Some(kind) = classify_declarator(child) else {
6346                    continue;
6347                };
6348                handled_declarator = true;
6349                match kind {
6350                    DeclaratorKind::Function(function_declarator) => {
6351                        handled_function = true;
6352                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6353                    }
6354                    DeclaratorKind::Variable(variable_declarator) => {
6355                        self.visit_variable_declaration(
6356                            node,
6357                            variable_declarator,
6358                            scope,
6359                            in_class_body,
6360                            ancestry,
6361                        );
6362                    }
6363                }
6364            }
6365        }
6366
6367        if handled_function {
6368            return;
6369        }
6370
6371        if !handled_declarator {
6372            if in_class_body {
6373                self.visit_class_members_from_declaration(node, scope, ancestry);
6374            } else {
6375                self.visit_global_variables_from_declaration(node, scope, ancestry);
6376            }
6377        }
6378    }
6379
6380    /// Preserve the member structure of an anonymous C aggregate.
6381    ///
6382    /// An anonymous union with no declarator promotes its fields into the
6383    /// containing aggregate. An anonymous struct/union followed by a named
6384    /// declarator, such as `struct { T *ops; } sock`, declares both the field
6385    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
6386    /// the declarator's structured nested identity so a later `value.sock.ops`
6387    /// chain can traverse it without parsing a type spelling (#2407).
6388    fn visit_c_anonymous_aggregate_declaration<'tree>(
6389        &mut self,
6390        node: Node<'tree>,
6391        scope: &ScopeInfo,
6392        in_class_body: bool,
6393        stack: &mut Vec<CppWork<'tree>>,
6394        ancestry: &ParentIndex<'tree>,
6395    ) -> bool {
6396        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
6397            return false;
6398        }
6399        let Some(aggregate) = node.child_by_field_name("type") else {
6400            return false;
6401        };
6402        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6403            || aggregate.child_by_field_name("name").is_some()
6404        {
6405            return false;
6406        }
6407        let Some(body) = cpp_body_node(aggregate) else {
6408            return false;
6409        };
6410
6411        let mut cursor = node.walk();
6412        let declarators = node
6413            .children_by_field_name("declarator", &mut cursor)
6414            .filter_map(|declarator| match classify_declarator(declarator) {
6415                Some(DeclaratorKind::Variable(variable)) => Some(variable),
6416                Some(DeclaratorKind::Function(_)) | None => None,
6417            })
6418            .collect::<Vec<_>>();
6419        if declarators.is_empty() {
6420            stack.push(CppWork::Container(CppContainer {
6421                node: body,
6422                scope: scope.clone(),
6423            }));
6424            return true;
6425        }
6426
6427        for declarator in declarators {
6428            let Some(name) = extract_variable_name(declarator, self.source) else {
6429                continue;
6430            };
6431            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6432            self.visit_named_class_like_shape(
6433                aggregate,
6434                name,
6435                Some(body),
6436                true,
6437                None,
6438                None,
6439                scope,
6440                stack,
6441                ancestry,
6442            );
6443        }
6444        true
6445    }
6446
6447    /// Give a function-local anonymous C aggregate a structured owner so its
6448    /// direct pointer bindings can be typed by the usage graph.  Unlike an
6449    /// anonymous aggregate in a class body, there is no source-level tag or
6450    /// typedef to supply an identity.  The aggregate's CST range is therefore
6451    /// part of a generated, collision-resistant identity; no source spelling
6452    /// is consulted.  The declaration's own variable remains a file-level
6453    /// field projection, while the aggregate body is visited as the generated
6454    /// class's field list.
6455    fn visit_c_anonymous_local_aggregate_declaration<'tree>(
6456        &mut self,
6457        node: Node<'tree>,
6458        scope: &ScopeInfo,
6459        stack: &mut Vec<CppWork<'tree>>,
6460        ancestry: &ParentIndex<'tree>,
6461    ) -> bool {
6462        if !self.c_tag_semantics || scope.class_unit.is_some() || !has_function_scope_ancestor(node)
6463        {
6464            return false;
6465        }
6466        let Some(aggregate) = node.child_by_field_name("type") else {
6467            return false;
6468        };
6469        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6470            || aggregate.child_by_field_name("name").is_some()
6471        {
6472            return false;
6473        }
6474        let Some(body) = cpp_body_node(aggregate) else {
6475            return false;
6476        };
6477        let mut cursor = node.walk();
6478        let declarators = node
6479            .children_by_field_name("declarator", &mut cursor)
6480            .filter_map(|declarator| match classify_declarator(declarator) {
6481                Some(DeclaratorKind::Variable(variable)) => Some(variable),
6482                Some(DeclaratorKind::Function(_)) | None => None,
6483            })
6484            .collect::<Vec<_>>();
6485        if declarators.is_empty() {
6486            return false;
6487        }
6488
6489        for declarator in &declarators {
6490            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
6491        }
6492        let name = format!("<anonymous:{}>", aggregate.start_byte());
6493        self.visit_named_class_like_shape(
6494            aggregate,
6495            name,
6496            Some(body),
6497            true,
6498            None,
6499            None,
6500            scope,
6501            stack,
6502            ancestry,
6503        );
6504        true
6505    }
6506
6507    fn visit_function_declaration<'tree>(
6508        &mut self,
6509        declaration_node: Node<'tree>,
6510        declarator: Node<'tree>,
6511        scope: &ScopeInfo,
6512        ancestry: &ParentIndex<'tree>,
6513    ) {
6514        let Some(function) = extract_function_info(declarator, self.source, scope) else {
6515            return;
6516        };
6517        let code_unit =
6518            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
6519        if self.parsed.contains_declaration(&code_unit) {
6520            self.parsed
6521                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
6522            return;
6523        }
6524        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6525        let signature = render_cpp_function_display_signature_from_node(
6526            declaration_node,
6527            self.source,
6528            scope.template_signature.as_deref(),
6529            false,
6530            ancestry,
6531        );
6532        self.parsed.add_signature_with_metadata(
6533            code_unit.clone(),
6534            cpp_signature_metadata(signature, declarator, self.source, ancestry)
6535                .with_declaration_only(true)
6536                .with_callable_linkage(cpp_callable_linkage(
6537                    declaration_node,
6538                    self.source,
6539                    ancestry,
6540                )),
6541        );
6542        if let Some(parent) = &scope.class_unit {
6543            self.parsed.add_child(parent.clone(), code_unit);
6544        } else if let Some(module) = &scope.module {
6545            self.parsed.add_child(module.clone(), code_unit);
6546        }
6547    }
6548
6549    fn visit_recovered_macro_qualified_function_declaration<'tree>(
6550        &mut self,
6551        declaration_node: Node<'tree>,
6552        call: Node<'tree>,
6553        scope: &ScopeInfo,
6554        ancestry: &ParentIndex<'tree>,
6555    ) {
6556        let Some(parent) = &scope.class_unit else {
6557            return;
6558        };
6559        let Some(name_node) = call.child_by_field_name("function") else {
6560            return;
6561        };
6562        let Some(arguments) = call.child_by_field_name("arguments") else {
6563            return;
6564        };
6565        let Some((signature, parameter_labels)) =
6566            recovered_macro_qualified_function_parameters(arguments, self.source)
6567        else {
6568            return;
6569        };
6570        let arity = parameter_labels.len();
6571        let function = FunctionInfo {
6572            package_name: scope.package_name.clone(),
6573            owner: Some(CppMemberOwner::Unit(parent.clone())),
6574            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
6575            signature,
6576        };
6577        if function.name.is_empty() {
6578            return;
6579        }
6580        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6581        if self.parsed.contains_declaration(&code_unit) {
6582            self.parsed
6583                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
6584            return;
6585        }
6586        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6587        let signature_label = render_cpp_function_display_signature_from_node(
6588            declaration_node,
6589            self.source,
6590            scope.template_signature.as_deref(),
6591            false,
6592            ancestry,
6593        );
6594        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6595            .with_declaration_only(true)
6596            .with_callable_arity(CallableArity::exact(arity))
6597            .with_callable_linkage(cpp_callable_linkage(
6598                declaration_node,
6599                self.source,
6600                ancestry,
6601            ));
6602        self.parsed
6603            .add_signature_with_metadata(code_unit.clone(), metadata);
6604        self.parsed.add_child(parent.clone(), code_unit);
6605    }
6606
6607    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
6608        &mut self,
6609        declaration_node: Node<'tree>,
6610        call: Node<'tree>,
6611        scope: &ScopeInfo,
6612        ancestry: &ParentIndex<'tree>,
6613    ) {
6614        let Some(parent) = &scope.class_unit else {
6615            return;
6616        };
6617        let Some(arguments) = call.child_by_field_name("arguments") else {
6618            return;
6619        };
6620        let Some((mut signature, parameter_labels)) =
6621            recovered_macro_qualified_function_parameters(arguments, self.source)
6622        else {
6623            return;
6624        };
6625        if let Some(template_signature) = &scope.template_signature {
6626            signature = format!("{template_signature}{signature}");
6627        }
6628        let arity = parameter_labels.len();
6629        let function = FunctionInfo {
6630            package_name: scope.package_name.clone(),
6631            owner: Some(CppMemberOwner::Unit(parent.clone())),
6632            name: parent.identifier().to_string(),
6633            signature,
6634        };
6635        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6636        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6637        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
6638        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
6639            .with_declaration_only(false)
6640            .with_callable_arity(CallableArity::exact(arity))
6641            .with_callable_linkage(cpp_callable_linkage(
6642                declaration_node,
6643                self.source,
6644                ancestry,
6645            ));
6646        self.parsed
6647            .add_signature_with_metadata(code_unit.clone(), metadata);
6648        self.parsed.add_child(parent.clone(), code_unit);
6649    }
6650
6651    fn visit_variable_declaration<'tree>(
6652        &mut self,
6653        declaration_node: Node<'tree>,
6654        declarator: Node<'tree>,
6655        scope: &ScopeInfo,
6656        in_class_body: bool,
6657        ancestry: &ParentIndex<'tree>,
6658    ) {
6659        let Some(name) = extract_variable_name(declarator, self.source) else {
6660            return;
6661        };
6662        let parent = if in_class_body {
6663            let Some(parent) = &scope.class_unit else {
6664                return;
6665            };
6666            Some(parent)
6667        } else {
6668            None
6669        };
6670        let short_name = match parent {
6671            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
6672            None => name.clone(),
6673        };
6674        let fq = cpp_leaf_fq(
6675            &scope.package_name,
6676            parent,
6677            &name,
6678            SegmentKind::Member,
6679            SegmentKind::Member,
6680        );
6681        let code_unit = CodeUnit::new_fq(
6682            self.file.clone(),
6683            CodeUnitType::Field,
6684            scope.package_name.clone(),
6685            short_name,
6686            fq,
6687        );
6688        if self.parsed.contains_declaration(&code_unit) {
6689            return;
6690        }
6691        self.add_declaration(code_unit.clone(), declaration_node, None, None);
6692        self.parsed.add_signature_with_metadata(
6693            code_unit.clone(),
6694            SignatureMetadata::new(
6695                render_cpp_field_signature(declaration_node, declarator, self.source),
6696                Vec::new(),
6697            )
6698            .with_cpp_field_linkage(cpp_field_declaration_linkage(
6699                declaration_node,
6700                self.source,
6701                ancestry,
6702            )),
6703        );
6704        if let Some(parent) = &scope.class_unit {
6705            self.parsed.add_child(parent.clone(), code_unit);
6706        } else if let Some(module) = &scope.module {
6707            self.parsed.add_child(module.clone(), code_unit);
6708        }
6709    }
6710
6711    fn visit_class_members_from_declaration<'tree>(
6712        &mut self,
6713        node: Node<'tree>,
6714        scope: &ScopeInfo,
6715        ancestry: &ParentIndex<'tree>,
6716    ) {
6717        let mut cursor = node.walk();
6718        for child in node.named_children(&mut cursor) {
6719            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
6720                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
6721            } else if child.kind() == "init_declarator"
6722                && let Some(inner) = child.child_by_field_name("declarator")
6723            {
6724                self.visit_variable_declaration(node, inner, scope, true, ancestry);
6725            } else if matches!(
6726                child.kind(),
6727                "identifier"
6728                    | "field_identifier"
6729                    | "pointer_declarator"
6730                    | "reference_declarator"
6731                    | "array_declarator"
6732                    | "parenthesized_declarator"
6733            ) {
6734                self.visit_variable_declaration(node, child, scope, true, ancestry);
6735            }
6736        }
6737    }
6738
6739    fn visit_global_variables_from_declaration<'tree>(
6740        &mut self,
6741        node: Node<'tree>,
6742        scope: &ScopeInfo,
6743        ancestry: &ParentIndex<'tree>,
6744    ) {
6745        let mut cursor = node.walk();
6746        for child in node.named_children(&mut cursor) {
6747            if child.kind() == "init_declarator"
6748                && let Some(inner) = child.child_by_field_name("declarator")
6749            {
6750                self.visit_variable_declaration(node, inner, scope, false, ancestry);
6751            } else if matches!(
6752                child.kind(),
6753                "identifier"
6754                    | "field_identifier"
6755                    | "pointer_declarator"
6756                    | "reference_declarator"
6757                    | "array_declarator"
6758                    | "parenthesized_declarator"
6759            ) {
6760                self.visit_variable_declaration(node, child, scope, false, ancestry);
6761            }
6762        }
6763    }
6764
6765    fn visit_type_declaration<'tree>(
6766        &mut self,
6767        node: Node<'tree>,
6768        scope: &ScopeInfo,
6769        stack: &mut Vec<CppWork<'tree>>,
6770        ancestry: &ParentIndex<'tree>,
6771    ) {
6772        let type_node = node.child_by_field_name("type");
6773        if let Some(type_node) = type_node
6774            && matches!(
6775                type_node.kind(),
6776                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6777            )
6778        {
6779            self.visit_class_like(type_node, scope, stack, ancestry);
6780        }
6781
6782        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
6783            let range = Range {
6784                start_byte: node.start_byte(),
6785                end_byte: recovered.end_node.end_byte(),
6786                start_line: node.start_position().row + 1,
6787                end_line: recovered.end_node.end_position().row + 1,
6788            };
6789            let signature = self
6790                .source
6791                .get(range.start_byte..range.end_byte)
6792                .map(normalize_cpp_whitespace)
6793                .unwrap_or_default();
6794            self.record_type_aliases(
6795                node,
6796                scope,
6797                vec![recovered.name],
6798                signature,
6799                range,
6800                ancestry,
6801            );
6802            return;
6803        }
6804
6805        let alias_names = match node.kind() {
6806            "alias_declaration" => extract_alias_declaration_name(node, self.source)
6807                .into_iter()
6808                .collect::<Vec<_>>(),
6809            "type_definition" => extract_typedef_alias_names(node, self.source),
6810            _ => Vec::new(),
6811        };
6812        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
6813            (type_node, alias_names.as_slice())
6814            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
6815            && type_node.child_by_field_name("name").is_none()
6816        {
6817            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
6818        } else {
6819            None
6820        };
6821        self.add_type_aliases(node, scope, alias_names, ancestry);
6822        if let Some((body, alias_name)) = anonymous_aggregate {
6823            // The typedef alias is also the only user-visible identity of an
6824            // anonymous aggregate. Reuse it as the member owner instead of
6825            // minting a second signatureless class with the same FQN. The
6826            // latter makes forward lookup ambiguous when conditional aliases
6827            // coexist and returns duplicate definitions even without guards.
6828            let signature = normalize_cpp_whitespace(node_text(node, self.source));
6829            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
6830            debug_assert!(self.parsed.contains_declaration(&alias_unit));
6831            let mut nested_scope = scope.clone();
6832            nested_scope.class_unit = Some(alias_unit);
6833            nested_scope.template_signature = scope.template_signature.clone();
6834            nested_scope.template_metadata = None;
6835            nested_scope.declarations_are_fields = false;
6836            nested_scope.recovered_specialization_member_scope = false;
6837            stack.push(CppWork::Container(CppContainer {
6838                node: body,
6839                scope: nested_scope,
6840            }));
6841        }
6842    }
6843
6844    fn add_type_aliases(
6845        &mut self,
6846        node: Node<'_>,
6847        scope: &ScopeInfo,
6848        alias_names: Vec<String>,
6849        ancestry: &ParentIndex<'_>,
6850    ) {
6851        let signature = normalize_cpp_whitespace(node_text(node, self.source));
6852        self.record_type_aliases(
6853            node,
6854            scope,
6855            alias_names,
6856            signature,
6857            cpp_declaration_range(node),
6858            ancestry,
6859        );
6860    }
6861
6862    fn record_type_aliases(
6863        &mut self,
6864        node: Node<'_>,
6865        scope: &ScopeInfo,
6866        alias_names: Vec<String>,
6867        signature: String,
6868        range: Range,
6869        ancestry: &ParentIndex<'_>,
6870    ) {
6871        if signature.is_empty() {
6872            return;
6873        }
6874        let type_name = node
6875            .child_by_field_name("type")
6876            .and_then(|type_node| type_node.child_by_field_name("name"))
6877            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
6878        for alias_name in alias_names {
6879            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
6880                continue;
6881            }
6882            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
6883            // Declaration identity does not include the alias signature. Keep
6884            // each physical range so conditional aliases retain their guards.
6885            self.add_declaration_with_range(code_unit.clone(), range, None, None);
6886            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
6887            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
6888                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
6889            });
6890            self.parsed.add_signature_with_metadata(
6891                code_unit.clone(),
6892                SignatureMetadata::new(signature.clone(), Vec::new())
6893                    .with_underlying_type_identity(underlying_type_identity),
6894            );
6895            if let Some(metadata) = &scope.template_metadata {
6896                let mut metadata = metadata.clone();
6897                metadata.primary_fq_name = code_unit.fq_name();
6898                self.parsed
6899                    .set_cpp_template_metadata(code_unit.clone(), metadata);
6900            }
6901            if let Some(parent) = &scope.class_unit {
6902                self.parsed.add_child(parent.clone(), code_unit.clone());
6903            } else if let Some(module) = &scope.module {
6904                self.parsed.add_child(module.clone(), code_unit.clone());
6905            }
6906            self.parsed.mark_type_alias(code_unit);
6907        }
6908    }
6909
6910    fn type_alias_unit(
6911        &self,
6912        scope: &ScopeInfo,
6913        alias_name: String,
6914        signature: String,
6915    ) -> CodeUnit {
6916        let short_name = if let Some(parent) = &scope.class_unit {
6917            cpp_join_nested_short(parent.short_name(), &alias_name)
6918        } else {
6919            alias_name.clone()
6920        };
6921        let fq = cpp_leaf_fq(
6922            &scope.package_name,
6923            scope.class_unit.as_ref(),
6924            &alias_name,
6925            SegmentKind::Nested,
6926            SegmentKind::Type,
6927        );
6928        CodeUnit::with_signature_and_fq(
6929            self.file.clone(),
6930            CodeUnitType::Class,
6931            scope.package_name.clone(),
6932            short_name,
6933            Some(signature),
6934            false,
6935            fq,
6936        )
6937    }
6938
6939    fn visit_macro(&mut self, node: Node<'_>) {
6940        let Some(name) = extract_macro_name(node, self.source) else {
6941            return;
6942        };
6943        let signature = node_text(node, self.source).trim_end().to_string();
6944        if signature.is_empty() {
6945            return;
6946        }
6947        let fq = cpp_member_fq("", &name);
6948        // A macro can be undefined and redefined later in the same file. Its
6949        // structured directive is part of the declaration identity so the
6950        // temporal environment can navigate to the definition active at a
6951        // reference instead of collapsing every spelling to the first range.
6952        // The same physical directive parsed through another C/C++ reading
6953        // still produces the same unit and remains deduplicated.
6954        let code_unit = CodeUnit::with_signature_and_fq(
6955            self.file.clone(),
6956            CodeUnitType::Macro,
6957            "",
6958            name.clone(),
6959            Some(signature.clone()),
6960            false,
6961            fq,
6962        );
6963        if !self.parsed.contains_declaration(&code_unit) {
6964            self.add_declaration(code_unit.clone(), node, None, None);
6965            let name_range = node
6966                .child_by_field_name("name")
6967                .map(cpp_declaration_range)
6968                .unwrap_or_else(|| cpp_declaration_range(node));
6969            self.parsed
6970                .record_materialization(MaterializationRecord::GeneratedDeclaration {
6971                    site: cpp_declaration_range(node),
6972                    argument: name_range,
6973                    kind: GenerationKind::PreprocessorDefinition,
6974                    unit: code_unit.clone(),
6975                });
6976            self.parsed.add_signature(code_unit, signature);
6977        }
6978        if node.kind() == "preproc_def" {
6979            update_object_macro_field_environment(
6980                node,
6981                self.source,
6982                &mut self.object_macro_fields,
6983                &mut self.ambiguous_object_macro_fields,
6984            );
6985        } else {
6986            self.object_macro_fields.remove(&name);
6987            self.ambiguous_object_macro_fields.remove(&name);
6988        }
6989    }
6990
6991    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
6992        let Some(directive) = node.child_by_field_name("directive") else {
6993            return;
6994        };
6995        let name = node_text(directive, self.source).trim();
6996        let range = cpp_declaration_range(node);
6997        self.materialize_object_macro_fields(name, range, scope);
6998    }
6999
7000    /// Bare object-like field-list macros inside an otherwise well-formed
7001    /// aggregate remain identifier nodes in a field declaration. More than
7002    /// one adjacent invocation can be folded into the same declaration, so
7003    /// inspect all of its structured identifier nodes.
7004    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7005        if !matches!(node.kind(), "declaration" | "field_declaration") {
7006            return false;
7007        }
7008        let macro_nodes =
7009            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7010        for macro_node in &macro_nodes {
7011            let name = node_text(*macro_node, self.source).trim();
7012            self.materialize_object_macro_fields(name, cpp_declaration_range(*macro_node), scope);
7013        }
7014        !macro_nodes.is_empty()
7015    }
7016
7017    fn materialize_object_macro_fields(&mut self, name: &str, range: Range, scope: &ScopeInfo) {
7018        let Some(fields) = self.object_macro_fields.get(name).cloned() else {
7019            return;
7020        };
7021        let Some(owner) = scope.class_unit.as_ref() else {
7022            return;
7023        };
7024        for field in fields {
7025            let signature = field.declaration.clone();
7026            let mut fq = owner.fq().clone();
7027            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7028            let short_name = if owner.short_name().is_empty() {
7029                field.name.clone()
7030            } else {
7031                format!("{}.{}", owner.short_name(), field.name)
7032            };
7033            let code_unit = CodeUnit::with_signature_and_fq(
7034                self.file.clone(),
7035                CodeUnitType::Field,
7036                owner.package_name().to_string(),
7037                short_name,
7038                Some(field.declaration),
7039                true,
7040                fq,
7041            );
7042            if self.parsed.contains_declaration(&code_unit) {
7043                continue;
7044            }
7045            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7046            self.parsed.add_signature(code_unit, signature);
7047        }
7048    }
7049
7050    /// Recover ordinary aggregates whose field-list macro invocations made
7051    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
7052    /// The class markers, aggregate braces, field nodes, and macro identifier
7053    /// nodes are all read from the AST; no source delimiter or token scan is
7054    /// used. A close-brace node inside a malformed field closes the innermost
7055    /// recovered aggregate, preserving nested-owner identity.
7056    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7057        let mut cursor = node.walk();
7058        let children = node.children(&mut cursor).collect::<Vec<_>>();
7059        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<Node<'_>>)>::new();
7060        let mut object_macro_fields = self.object_macro_fields.clone();
7061        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7062        let mut open = Vec::<usize>::new();
7063        let mut index = 0;
7064        while index < children.len() {
7065            let keyword = children[index];
7066            if update_object_macro_field_environment(
7067                keyword,
7068                self.source,
7069                &mut object_macro_fields,
7070                &mut ambiguous_object_macro_fields,
7071            ) {
7072                index += 1;
7073                continue;
7074            }
7075            if index + 2 < children.len()
7076                && matches!(keyword.kind(), "struct" | "class" | "union")
7077                && matches!(children[index + 1].kind(), "type_identifier" | "identifier")
7078                && children[index + 2].kind() == "{"
7079            {
7080                let name_node = children[index + 1];
7081                let opening = children[index + 2];
7082                let name = normalize_cpp_whitespace(node_text(name_node, self.source));
7083                if !name.is_empty() {
7084                    let parent = open
7085                        .last()
7086                        .and_then(|class| recovered.get(*class))
7087                        .map(|(owner, _, _, _)| owner.clone())
7088                        .or_else(|| scope.class_unit.clone());
7089                    let short_name = parent.as_ref().map_or_else(
7090                        || name.clone(),
7091                        |parent| cpp_join_nested_short(parent.short_name(), &name),
7092                    );
7093                    let fq = cpp_leaf_fq(
7094                        &scope.package_name,
7095                        parent.as_ref(),
7096                        &name,
7097                        SegmentKind::Nested,
7098                        SegmentKind::Type,
7099                    );
7100                    let owner = CodeUnit::with_signature_and_fq(
7101                        self.file.clone(),
7102                        CodeUnitType::Class,
7103                        scope.package_name.clone(),
7104                        short_name,
7105                        None,
7106                        false,
7107                        fq,
7108                    );
7109                    recovered.push((owner, keyword.start_byte(), opening.end_byte(), Vec::new()));
7110                    open.push(recovered.len() - 1);
7111                    index += 3;
7112                    continue;
7113                }
7114            }
7115            if let Some(&class_index) = open.last()
7116                && children[index].kind() == "field_declaration"
7117            {
7118                let field = children[index];
7119                recovered[class_index]
7120                    .3
7121                    .extend(object_macro_identifier_nodes_with_environment(
7122                        field,
7123                        self.source,
7124                        &mut object_macro_fields,
7125                        &mut ambiguous_object_macro_fields,
7126                    ));
7127                let end = field.end_byte();
7128                for &open_class in &open {
7129                    recovered[open_class].2 = recovered[open_class].2.max(end);
7130                }
7131                let closes = count_close_brace_nodes(field);
7132                for _ in 0..closes {
7133                    if let Some(closed) = open.pop() {
7134                        recovered[closed].2 = end;
7135                    }
7136                }
7137            }
7138            index += 1;
7139        }
7140
7141        let mut owners = Vec::with_capacity(recovered.len());
7142        for (owner, start, end, macro_nodes) in recovered {
7143            let range = Range {
7144                start_byte: start,
7145                end_byte: end,
7146                start_line: self.source.get(..start).map_or(1, |source| {
7147                    source.bytes().filter(|byte| *byte == b'\n').count() + 1
7148                }),
7149                end_line: self.source.get(..end).map_or(1, |source| {
7150                    source.bytes().filter(|byte| *byte == b'\n').count() + 1
7151                }),
7152            };
7153            if !self.parsed.contains_declaration(&owner) {
7154                let parent = owners
7155                    .iter()
7156                    .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7157                    .cloned()
7158                    .or_else(|| scope.class_unit.clone());
7159                self.add_declaration_with_range(owner.clone(), range, parent, None);
7160            }
7161            let owner_scope = ScopeInfo {
7162                class_unit: Some(owner.clone()),
7163                declarations_are_fields: true,
7164                ..scope.clone()
7165            };
7166            for macro_node in macro_nodes {
7167                let name = normalize_cpp_whitespace(node_text(macro_node, self.source));
7168                self.materialize_object_macro_fields(
7169                    &name,
7170                    cpp_declaration_range(macro_node),
7171                    &owner_scope,
7172                );
7173            }
7174            owners.push(owner);
7175        }
7176    }
7177
7178    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7179        let Some(_directive) = node.child_by_field_name("directive") else {
7180            return;
7181        };
7182        if is_cpp_undef_directive(node, self.source) {
7183            update_object_macro_field_environment(
7184                node,
7185                self.source,
7186                &mut self.object_macro_fields,
7187                &mut self.ambiguous_object_macro_fields,
7188            );
7189            return;
7190        }
7191        let directly_in_field_list = node
7192            .parent()
7193            .is_some_and(|parent| parent.kind() == "field_declaration_list");
7194        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
7195            self.visit_object_macro_fields(node, scope);
7196        }
7197    }
7198}
7199
7200/// Apply one preprocessor directive to an object-like field-list environment.
7201/// The environment is intentionally separate from the declaration visitor so
7202/// malformed parser regions can replay directives in source order without
7203/// mutating the live walk's state ahead of those directives.
7204fn update_object_macro_field_environment(
7205    node: Node<'_>,
7206    source: &str,
7207    fields: &mut HashMap<String, Vec<MacroReplacementField>>,
7208    ambiguous: &mut HashSet<String>,
7209) -> bool {
7210    match node.kind() {
7211        "preproc_def" => {
7212            let Some(name) = extract_macro_name(node, source) else {
7213                return false;
7214            };
7215            let replacement = node
7216                .child_by_field_name("value")
7217                .map(|value| {
7218                    crate::graph::syntax::object_macro_replacement_fields(node_text(value, source))
7219                })
7220                .unwrap_or_default();
7221            if replacement.is_empty() || ambiguous.contains(&name) {
7222                fields.remove(&name);
7223                ambiguous.insert(name);
7224            } else if let Some(previous) = fields.get(&name) {
7225                if previous != &replacement {
7226                    fields.remove(&name);
7227                    ambiguous.insert(name);
7228                }
7229            } else {
7230                fields.insert(name, replacement);
7231            }
7232            true
7233        }
7234        "preproc_call" if is_cpp_undef_directive(node, source) => {
7235            if let Some(argument) = node.child_by_field_name("argument") {
7236                let name = node_text(argument, source).trim();
7237                fields.remove(name);
7238                if inside_preprocessor_conditional(node) {
7239                    ambiguous.insert(name.to_string());
7240                } else {
7241                    ambiguous.remove(name);
7242                }
7243            }
7244            true
7245        }
7246        _ => false,
7247    }
7248}
7249
7250fn object_macro_identifier_nodes<'tree>(
7251    node: Node<'tree>,
7252    source: &str,
7253    fields: &HashMap<String, Vec<MacroReplacementField>>,
7254) -> Vec<Node<'tree>> {
7255    let mut result = Vec::new();
7256    let mut stack = vec![node];
7257    while let Some(current) = stack.pop() {
7258        if matches!(
7259            current.kind(),
7260            "identifier" | "field_identifier" | "type_identifier"
7261        ) && fields.contains_key(node_text(current, source).trim())
7262        {
7263            result.push(current);
7264        }
7265        let mut cursor = current.walk();
7266        let children = current.children(&mut cursor).collect::<Vec<_>>();
7267        stack.extend(children.into_iter().rev());
7268    }
7269    result.sort_by_key(|node| node.start_byte());
7270    result
7271}
7272
7273/// Collect macro identifiers while replaying any nested preprocessor
7274/// directives in source order. Recovery regions can contain a later `#undef`
7275/// in the same parser error envelope; using the live visitor map for the
7276/// entire envelope would incorrectly materialize invocations after it.
7277fn object_macro_identifier_nodes_with_environment<'tree>(
7278    node: Node<'tree>,
7279    source: &str,
7280    fields: &mut HashMap<String, Vec<MacroReplacementField>>,
7281    ambiguous: &mut HashSet<String>,
7282) -> Vec<Node<'tree>> {
7283    let mut result = Vec::new();
7284    let mut stack = vec![node];
7285    while let Some(current) = stack.pop() {
7286        if update_object_macro_field_environment(current, source, fields, ambiguous) {
7287            continue;
7288        }
7289        if matches!(
7290            current.kind(),
7291            "identifier" | "field_identifier" | "type_identifier"
7292        ) && fields.contains_key(node_text(current, source).trim())
7293        {
7294            result.push(current);
7295        }
7296        let mut cursor = current.walk();
7297        let children = current.children(&mut cursor).collect::<Vec<_>>();
7298        stack.extend(children.into_iter().rev());
7299    }
7300    result.sort_by_key(|node| node.start_byte());
7301    result
7302}
7303
7304fn count_close_brace_nodes(node: Node<'_>) -> usize {
7305    let mut count = 0;
7306    let mut stack = vec![node];
7307    while let Some(current) = stack.pop() {
7308        if current.kind() == "}" && !current.is_missing() {
7309            count += 1;
7310        }
7311        let mut cursor = current.walk();
7312        stack.extend(current.children(&mut cursor));
7313    }
7314    count
7315}
7316
7317/// Classify a C++ field while its declaration syntax is already available.
7318///
7319/// The persisted result lets later visibility queries avoid reparsing the
7320/// complete source file only to recover linkage.
7321pub fn cpp_field_declaration_linkage<'tree>(
7322    declaration: Node<'tree>,
7323    source: &str,
7324    ancestry: &ParentIndex<'tree>,
7325) -> CppFieldLinkage {
7326    let mut current = ancestry.parent(declaration);
7327    let mut enclosed_by_class = false;
7328    while let Some(node) = current {
7329        if node.kind() == "namespace_definition"
7330            && node
7331                .child_by_field_name("name")
7332                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7333        {
7334            return CppFieldLinkage::Internal;
7335        }
7336        if matches!(
7337            node.kind(),
7338            "class_specifier" | "struct_specifier" | "union_specifier"
7339        ) && node
7340            .child_by_field_name("name")
7341            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7342        {
7343            return CppFieldLinkage::Internal;
7344        }
7345        if matches!(
7346            node.kind(),
7347            "class_specifier" | "struct_specifier" | "union_specifier"
7348        ) {
7349            enclosed_by_class = true;
7350        }
7351        if matches!(node.kind(), "function_definition" | "lambda_expression") {
7352            return CppFieldLinkage::Internal;
7353        }
7354        current = ancestry.parent(node);
7355    }
7356    if enclosed_by_class {
7357        return CppFieldLinkage::External;
7358    }
7359    let mut cursor = declaration.walk();
7360    let mut has_static = false;
7361    let mut has_extern = false;
7362    let mut has_inline = false;
7363    let mut has_const = false;
7364    let mut has_constexpr = false;
7365    for child in declaration.named_children(&mut cursor) {
7366        let text = normalize_cpp_whitespace(node_text(child, source));
7367        match (child.kind(), text.as_str()) {
7368            ("storage_class_specifier", "static") => has_static = true,
7369            ("storage_class_specifier", "extern") => has_extern = true,
7370            ("storage_class_specifier", "inline") => has_inline = true,
7371            ("storage_class_specifier", "constexpr") => has_constexpr = true,
7372            ("type_qualifier", "const") => has_const = true,
7373            ("type_qualifier", "constexpr") => has_constexpr = true,
7374            _ => {}
7375        }
7376    }
7377    if has_static {
7378        CppFieldLinkage::Internal
7379    } else if has_extern || has_inline {
7380        CppFieldLinkage::External
7381    } else if has_const || has_constexpr {
7382        CppFieldLinkage::InternalUnlessExternalPeer
7383    } else {
7384        CppFieldLinkage::External
7385    }
7386}
7387
7388fn cpp_declaration_range(node: Node<'_>) -> Range {
7389    Range {
7390        start_byte: node.start_byte(),
7391        end_byte: node.end_byte(),
7392        start_line: node.start_position().row + 1,
7393        end_line: node.end_position().row + 1,
7394    }
7395}
7396
7397/// A recovery interval as a [`Range`], for materialization records whose
7398/// window is a byte region rather than one parser node (the sentinel-macro
7399/// region reparses, issue #941/#1657).
7400fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
7401    let line_at = |byte: usize| {
7402        source.as_bytes()[..byte]
7403            .iter()
7404            .filter(|&&b| b == b'\n')
7405            .count()
7406            + 1
7407    };
7408    Range {
7409        start_byte,
7410        end_byte,
7411        start_line: line_at(start_byte),
7412        end_line: line_at(end_byte),
7413    }
7414}
7415
7416/// Every `#include` directive the tree holds, in source order.
7417///
7418/// A preorder sweep rather than a step of the declaration walk: the container
7419/// walk descends only through declaration scopes, so an include written inside
7420/// a class body or a function body would otherwise never be seen, and an
7421/// include is an include wherever it is written.
7422pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
7423    walk_named_tree_preorder(root, true, |node| {
7424        if node.kind() == "preproc_include" {
7425            let raw = normalize_cpp_whitespace(node_text(node, source));
7426            if !raw.is_empty() {
7427                parsed.imports.push(ImportInfo {
7428                    raw_snippet: raw,
7429                    is_wildcard: false,
7430                    is_global: false,
7431                    identifier: None,
7432                    alias: None,
7433                    path: None,
7434                    binder_span: None,
7435                });
7436            }
7437            return WalkControl::SkipChildren;
7438        }
7439        WalkControl::Continue
7440    });
7441}
7442
7443pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
7444    let mut in_block_comment = false;
7445    for line in source.lines() {
7446        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
7447        let trimmed = stripped.trim();
7448        if !looks_like_quoted_include_line(trimmed) {
7449            continue;
7450        }
7451
7452        let raw = normalize_cpp_whitespace(trimmed);
7453        // The tree-sitter walk already recorded every `#include` it could see;
7454        // this line scan only recovers the ones a parse error hid, so skip a
7455        // snippet that is already an import binding.
7456        if parsed
7457            .imports
7458            .iter()
7459            .any(|import| import.raw_snippet == raw)
7460        {
7461            continue;
7462        }
7463
7464        parsed.imports.push(ImportInfo {
7465            raw_snippet: raw,
7466            is_wildcard: false,
7467            is_global: false,
7468            identifier: None,
7469            alias: None,
7470            path: None,
7471            binder_span: None,
7472        });
7473    }
7474}
7475
7476fn looks_like_quoted_include_line(line: &str) -> bool {
7477    let Some(rest) = line.trim_start().strip_prefix('#') else {
7478        return false;
7479    };
7480    let Some(rest) = rest.trim_start().strip_prefix("include") else {
7481        return false;
7482    };
7483    rest.trim_start().starts_with('"')
7484}
7485
7486fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
7487    let mut raw = Vec::new();
7488    let mut cursor = node.walk();
7489    for child in node.named_children(&mut cursor) {
7490        if child.kind() == "base_class_clause" {
7491            collect_cpp_base_nodes(child, source, &mut raw);
7492        }
7493    }
7494    raw
7495}
7496
7497fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
7498    walk_named_tree_preorder(node, false, |child| match child.kind() {
7499        "type_identifier" | "qualified_identifier" | "template_type" => {
7500            let text = normalize_cpp_whitespace(node_text(child, source));
7501            if !text.is_empty() {
7502                raw.push(text);
7503            }
7504            WalkControl::SkipChildren
7505        }
7506        _ => WalkControl::Continue,
7507    });
7508}
7509
7510fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
7511    let mut out = String::new();
7512    let chars: Vec<char> = line.chars().collect();
7513    let mut index = 0;
7514    let mut in_string = false;
7515    let mut in_char = false;
7516    let mut escape = false;
7517
7518    while index < chars.len() {
7519        let ch = chars[index];
7520        let next = chars.get(index + 1).copied();
7521
7522        if *in_block_comment {
7523            if ch == '*' && next == Some('/') {
7524                *in_block_comment = false;
7525                index += 2;
7526            } else {
7527                index += 1;
7528            }
7529            continue;
7530        }
7531
7532        if in_string {
7533            out.push(ch);
7534            if escape {
7535                escape = false;
7536            } else if ch == '\\' {
7537                escape = true;
7538            } else if ch == '"' {
7539                in_string = false;
7540            }
7541            index += 1;
7542            continue;
7543        }
7544
7545        if in_char {
7546            out.push(ch);
7547            if escape {
7548                escape = false;
7549            } else if ch == '\\' {
7550                escape = true;
7551            } else if ch == '\'' {
7552                in_char = false;
7553            }
7554            index += 1;
7555            continue;
7556        }
7557
7558        if ch == '/' && next == Some('/') {
7559            break;
7560        }
7561        if ch == '/' && next == Some('*') {
7562            *in_block_comment = true;
7563            index += 2;
7564            continue;
7565        }
7566        if ch == '"' {
7567            in_string = true;
7568            out.push(ch);
7569            index += 1;
7570            continue;
7571        }
7572        if ch == '\'' {
7573            in_char = true;
7574            out.push(ch);
7575            index += 1;
7576            continue;
7577        }
7578
7579        out.push(ch);
7580        index += 1;
7581    }
7582
7583    out
7584}
7585
7586#[derive(Clone)]
7587struct FunctionInfo {
7588    package_name: String,
7589    owner: Option<CppMemberOwner>,
7590    name: String,
7591    signature: String,
7592}
7593
7594/// Owner of a member function, kept structured so a literal `$` inside a
7595/// source-level class name never crosses a join/split boundary: the legacy
7596/// `$`-joined owner string was re-split at fq construction, dropping a leading
7597/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
7598/// and tripping the package/short boundary assert -- the #2140 corruption one
7599/// level up (#2362).
7600#[derive(Clone)]
7601enum CppMemberOwner {
7602    /// Source-level owner class chain from a qualified declarator-id, one
7603    /// class name per component (`Outer::Inner::method` -> `["Outer",
7604    /// "Inner"]`); each component may itself contain a literal `$`.
7605    Chain(Vec<String>),
7606    /// The lexically enclosing or recovered class unit; the member fq extends
7607    /// its fq directly instead of re-splitting its `$`-joined short chain.
7608    Unit(CodeUnit),
7609}
7610
7611impl CppMemberOwner {
7612    /// The legacy `$`-joined owner chain used in the member's short name.
7613    fn short_chain(&self) -> String {
7614        match self {
7615            Self::Chain(chain) => chain.join("$"),
7616            Self::Unit(parent) => parent.short_name().to_string(),
7617        }
7618    }
7619}
7620
7621enum DeclaratorKind<'a> {
7622    Function(Node<'a>),
7623    Variable(Node<'a>),
7624}
7625
7626impl FunctionInfo {
7627    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
7628        self.code_unit_with_synthetic(file, false)
7629    }
7630
7631    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
7632        let short_name = match &self.owner {
7633            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
7634            None => self.name.clone(),
7635        };
7636        let fq = match &self.owner {
7637            Some(CppMemberOwner::Chain(chain)) => {
7638                debug_assert!(
7639                    !chain.is_empty(),
7640                    "an empty owner chain is no owner; producers return None instead"
7641                );
7642                let mut fq = FqName::new();
7643                cpp_push_package(&mut fq, &self.package_name);
7644                let mut first = true;
7645                for component in chain {
7646                    let kind = if first {
7647                        SegmentKind::Type
7648                    } else {
7649                        SegmentKind::Nested
7650                    };
7651                    fq.push(cpp_segment(component, kind));
7652                    first = false;
7653                }
7654                fq.push(cpp_segment(&self.name, SegmentKind::Member));
7655                fq
7656            }
7657            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
7658                .fq()
7659                .clone()
7660                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
7661            // An anonymous parent (empty short chain) contributes no owner
7662            // segment -- the same guard as cpp_join_member_short above.
7663            Some(CppMemberOwner::Unit(_)) | None => {
7664                let mut fq = FqName::new();
7665                cpp_push_package(&mut fq, &self.package_name);
7666                fq.push(cpp_segment(&self.name, SegmentKind::Member));
7667                fq
7668            }
7669        };
7670        CodeUnit::with_signature_and_fq(
7671            file,
7672            CodeUnitType::Function,
7673            self.package_name.clone(),
7674            short_name,
7675            Some(self.signature.clone()),
7676            synthetic,
7677            fq,
7678        )
7679    }
7680}
7681
7682fn extract_function_info(
7683    declarator: Node<'_>,
7684    source: &str,
7685    scope: &ScopeInfo,
7686) -> Option<FunctionInfo> {
7687    let parameters_node = declarator.child_by_field_name("parameters")?;
7688    let declarator_name_node = declarator
7689        .child_by_field_name("declarator")
7690        .or_else(|| parameters_node.prev_named_sibling())?;
7691    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
7692}
7693
7694fn extract_function_info_from_name(
7695    declarator: Node<'_>,
7696    declarator_name_node: Node<'_>,
7697    source: &str,
7698    scope: &ScopeInfo,
7699) -> Option<FunctionInfo> {
7700    let parameters_node = declarator.child_by_field_name("parameters")?;
7701    let parameters_text = cpp_parameter_signature(parameters_node, source);
7702    let recovered_specialization_member = scope
7703        .recovered_specialization_member_scope
7704        .then(|| {
7705            let terminal = declarator_name_node
7706                .child_by_field_name("name")
7707                .unwrap_or(declarator_name_node);
7708            let name = canonical_cpp_qualified_component(terminal, source)?.name;
7709            let owner = scope.class_unit.as_ref()?;
7710            Some((
7711                Some(CppMemberOwner::Unit(owner.clone())),
7712                name,
7713                scope.package_name.clone(),
7714            ))
7715        })
7716        .flatten();
7717    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
7718        parts
7719    } else if let Some(parts) =
7720        split_structured_templated_cpp_name(declarator_name_node, source, scope)
7721    {
7722        parts
7723    } else {
7724        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
7725            declarator_name_node,
7726            source,
7727        )?);
7728        if raw_name.is_empty() {
7729            return None;
7730        }
7731        split_cpp_name(&raw_name, scope)
7732    };
7733    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
7734    let mut signature = if suffix.is_empty() {
7735        parameters_text
7736    } else {
7737        format!("{parameters_text} {suffix}")
7738    };
7739    if let Some(template_signature) = &scope.template_signature {
7740        signature = format!("{template_signature}{signature}");
7741    }
7742
7743    Some(FunctionInfo {
7744        package_name,
7745        owner,
7746        name,
7747        signature,
7748    })
7749}
7750
7751/// Recover the semantic return type and callable name when a declaration macro
7752/// occupies a function definition's `type` field. Tree-sitter either exposes a
7753/// scalar return as the declarator's apparent name and the callable as the sole
7754/// identifier in an `ERROR`, or joins a template return and callable into a
7755/// qualified identifier with a missing `::`. Both shapes retain the complete
7756/// parameter list and body; a concrete separator remains an out-of-line member.
7757fn cpp_macro_displaced_callable_parts<'tree>(
7758    function_declarator: Node<'tree>,
7759    source: &str,
7760    ancestry: &ParentIndex<'tree>,
7761) -> Option<(Node<'tree>, Node<'tree>)> {
7762    let definition = ancestry.parent(function_declarator)?;
7763    if definition.kind() != "function_definition"
7764        || definition.child_by_field_name("declarator") != Some(function_declarator)
7765        || definition
7766            .child_by_field_name("body")
7767            .is_none_or(|body| body.kind() != "compound_statement")
7768    {
7769        return None;
7770    }
7771    let macro_type = definition.child_by_field_name("type")?;
7772    if macro_type.kind() != "type_identifier"
7773        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
7774    {
7775        return None;
7776    }
7777
7778    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
7779    if apparent_return_type.kind() == "qualified_identifier"
7780        && let (Some(return_type), Some(callable_name)) = (
7781            apparent_return_type.child_by_field_name("scope"),
7782            apparent_return_type.child_by_field_name("name"),
7783        )
7784        && return_type.kind() == "template_type"
7785        && matches!(callable_name.kind(), "identifier" | "field_identifier")
7786        && (0..apparent_return_type.child_count())
7787            .filter_map(|index| apparent_return_type.child(index))
7788            .any(|child| child.kind() == "::" && child.is_missing())
7789        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
7790        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
7791    {
7792        return Some((return_type, callable_name));
7793    }
7794    if !matches!(
7795        apparent_return_type.kind(),
7796        "identifier" | "field_identifier" | "type_identifier"
7797    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
7798    {
7799        return None;
7800    }
7801    let parameters = function_declarator.child_by_field_name("parameters")?;
7802    let mut cursor = function_declarator.walk();
7803    let between = function_declarator
7804        .named_children(&mut cursor)
7805        .filter(|child| child.kind() != "comment")
7806        .filter(|child| {
7807            child.start_byte() >= apparent_return_type.end_byte()
7808                && child.end_byte() <= parameters.start_byte()
7809                && !same_node(*child, apparent_return_type)
7810                && !same_node(*child, parameters)
7811        })
7812        .collect::<Vec<_>>();
7813    let [name_error] = between.as_slice() else {
7814        return None;
7815    };
7816    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
7817        return None;
7818    }
7819    let callable_name = name_error.named_child(0)?;
7820    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
7821        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
7822    {
7823        return None;
7824    }
7825    Some((apparent_return_type, callable_name))
7826}
7827
7828/// The part of a `function_declarator` after its parameter list that belongs to
7829/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
7830/// specification, a trailing return type and a trailing requires-clause.
7831///
7832/// The grammar makes each of these a distinct sibling of the `parameters`
7833/// field, so they are read from the tree. Splitting the declarator's text on
7834/// the parameter list instead silently dropped every qualifier whenever the
7835/// parameter list was spelled with whitespace that normalization rewrote - a
7836/// line break or a double space was enough to make a `const` member definition
7837/// a different logical symbol from its declaration (#1827).
7838///
7839/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
7840/// are deliberately excluded. C++ does not make them part of the signature and
7841/// an out-of-line definition never repeats them, so including them would split
7842/// a declaration from its own definition.
7843fn cpp_declarator_identity_suffix(
7844    declarator: Node<'_>,
7845    parameters_node: Node<'_>,
7846    source: &str,
7847) -> String {
7848    let mut cursor = declarator.walk();
7849    let parts = declarator
7850        .named_children(&mut cursor)
7851        .filter(|child| child.start_byte() >= parameters_node.end_byte())
7852        .filter(|child| {
7853            matches!(
7854                child.kind(),
7855                "type_qualifier"
7856                    | "ref_qualifier"
7857                    | "noexcept"
7858                    | "throw_specifier"
7859                    | "trailing_return_type"
7860                    | "requires_clause"
7861            )
7862        })
7863        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
7864        .filter(|text| !text.is_empty())
7865        .collect::<Vec<_>>();
7866    normalize_cpp_qualifier_suffix(&parts.join(" "))
7867}
7868
7869/// The identity suffix of one callable declarator, for a consumer that holds
7870/// the declarator rather than the declaration walk's parts.
7871///
7872/// The persisted signature concatenates the parameter spelling and this suffix,
7873/// so a comparison that must agree on the suffix alone recomputes it here
7874/// instead of splitting the stored string.
7875pub(crate) fn cpp_callable_identity_suffix(
7876    function_declarator: Node<'_>,
7877    source: &str,
7878) -> Option<String> {
7879    let parameters_node = function_declarator.child_by_field_name("parameters")?;
7880    Some(cpp_declarator_identity_suffix(
7881        function_declarator,
7882        parameters_node,
7883        source,
7884    ))
7885}
7886
7887pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
7888    match classify_declarator(node)? {
7889        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
7890        DeclaratorKind::Variable(_) => None,
7891    }
7892}
7893
7894fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
7895    match node.kind() {
7896        "function_declarator" => {
7897            let inner = node
7898                .child_by_field_name("declarator")
7899                .or_else(|| node.child_by_field_name("name"))
7900                .or_else(|| last_named_child(node));
7901            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
7902                Some(DeclaratorKind::Variable(node))
7903            } else {
7904                Some(DeclaratorKind::Function(node))
7905            }
7906        }
7907        "init_declarator"
7908        | "pointer_declarator"
7909        | "reference_declarator"
7910        | "parenthesized_declarator"
7911        | "array_declarator"
7912        | "attributed_declarator"
7913        | "template_function" => node
7914            .child_by_field_name("declarator")
7915            .or_else(|| node.child_by_field_name("name"))
7916            .or_else(|| last_named_child(node))
7917            .and_then(classify_declarator),
7918        "identifier" | "field_identifier" | "qualified_identifier" => {
7919            Some(DeclaratorKind::Variable(node))
7920        }
7921        _ => node
7922            .child_by_field_name("declarator")
7923            .or_else(|| node.child_by_field_name("name"))
7924            .or_else(|| last_named_child(node))
7925            .and_then(classify_declarator),
7926    }
7927}
7928
7929fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
7930    matches!(
7931        node.kind(),
7932        "function_declarator"
7933            | "init_declarator"
7934            | "pointer_declarator"
7935            | "reference_declarator"
7936            | "parenthesized_declarator"
7937            | "array_declarator"
7938            | "attributed_declarator"
7939            | "template_function"
7940            | "identifier"
7941            | "field_identifier"
7942            | "qualified_identifier"
7943    )
7944}
7945
7946fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
7947    let class_like = first_class_like_child(node);
7948    let mut cursor = node.walk();
7949    node.named_children(&mut cursor).any(|child| {
7950        matches!(
7951            child.kind(),
7952            "init_declarator"
7953                | "pointer_declarator"
7954                | "reference_declarator"
7955                | "array_declarator"
7956                | "function_declarator"
7957                | "parenthesized_declarator"
7958                | "attributed_declarator"
7959        ) || matches!(
7960            child.kind(),
7961            "identifier" | "field_identifier" | "qualified_identifier"
7962        ) && class_like.is_none_or(|class_node| {
7963            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
7964        })
7965    })
7966}
7967
7968/// One namespace-scope forward class declaration that a recovered export-macro
7969/// class definition may borrow its identity from.  Tree-sitter can close a
7970/// malformed class at the enclosing namespace's closing brace, leaving the later
7971/// class definitions as root-level recovered `function_definition` nodes.  A
7972/// preceding `class Name;` in the same namespace is the only structured identity
7973/// signal available in that shape.
7974///
7975/// Everything recorded here is a property of the forward declaration alone.
7976/// What depends on the node doing the asking -- that the forward and its
7977/// namespace both close before it, with nothing but recovery trivia between --
7978/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
7979/// tree answers every later question about it.
7980struct CppNamespaceForward {
7981    name: String,
7982    start_byte: usize,
7983    /// Where the malformed namespace that held the forward ended.  No query
7984    /// asks anything else about that node.
7985    namespace_end_byte: usize,
7986    package_name: String,
7987}
7988
7989/// Read `node` as a borrowable namespace forward declaration.
7990///
7991/// The admission is deliberately conservative: only a body-less class specifier
7992/// whose declaration has no declarator, at namespace scope rather than inside a
7993/// function or class body, in a namespace that itself failed to parse.
7994fn cpp_namespace_forward_entry<'tree>(
7995    node: Node<'tree>,
7996    source: &str,
7997    ancestry: &ParentIndex<'tree>,
7998) -> Option<CppNamespaceForward> {
7999    if !matches!(
8000        node.kind(),
8001        "class_specifier" | "struct_specifier" | "union_specifier"
8002    ) || cpp_body_node(node).is_some()
8003    {
8004        return None;
8005    }
8006    let parent = node.parent()?;
8007    if !(parent.kind() == "declaration_list"
8008        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
8009    {
8010        return None;
8011    }
8012    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
8013    // Borrowing is only justified by the parser-recovery shape we are
8014    // repairing: the namespace that held the forward must itself contain a
8015    // syntax error. A clean, unrelated namespace forward is not an identity
8016    // proof.
8017    if !namespace.has_error() {
8018        return None;
8019    }
8020    Some(CppNamespaceForward {
8021        name: class_like_name(node, source, ancestry)?,
8022        start_byte: node.start_byte(),
8023        namespace_end_byte: namespace.end_byte(),
8024        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
8025    })
8026}
8027
8028/// Whether `forward` stands in the recovery relation to the node asking about
8029/// it: it and its malformed namespace both closed before the recovered class,
8030/// and nothing but recovery trivia separates the two.
8031fn cpp_namespace_forward_matches_recovery(
8032    forward: &CppNamespaceForward,
8033    recovered_node: Node<'_>,
8034) -> bool {
8035    forward.start_byte < recovered_node.start_byte()
8036        && forward.namespace_end_byte < recovered_node.start_byte()
8037        && malformed_namespace_is_nearest_recovery_region(
8038            forward.namespace_end_byte,
8039            recovered_node,
8040        )
8041}
8042
8043/// What one open [`CppVisitor::record_recovered_declarations`] has watched
8044/// happen to the declaration set.
8045///
8046/// The recovered set used to be a difference against a clone of the whole
8047/// declaration set, taken once per recovery: O(recoveries x declarations) on
8048/// exactly the error-recovered files that already walk slowest (#2787). The
8049/// walk knows which declarations it creates, so the capture collects them as
8050/// they are made and the difference is never needed.
8051///
8052/// `removed_pre_existing` is what makes that equal to the difference. A
8053/// deferred replacement removes the replaced declaration's children
8054/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
8055/// re-creates them. Creation alone cannot tell that apart from a first mint, so
8056/// a removal of a declaration this capture did not create records that it was
8057/// already there when the capture opened.
8058#[derive(Debug, Default)]
8059pub struct CppRecoveryCapture {
8060    /// Declarations created while this capture was open, in creation order.
8061    created: Vec<CodeUnit>,
8062    /// Membership for `created`.
8063    created_units: HashSet<CodeUnit>,
8064    /// Declarations that predate this capture and have been removed during it.
8065    removed_pre_existing: HashSet<CodeUnit>,
8066}
8067
8068/// Which owners the parse product already holds field declarations for, folded
8069/// in as the walk records them.
8070///
8071/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
8072/// and used to answer it by scanning every declaration accumulated so far:
8073/// O(enums x declarations) on exactly the generated headers that declare many
8074/// of both (#2786). The answer only grows by declaration, so the walk carries
8075/// it. A field's short name names its owner chain, `Owner.member`, so one field
8076/// answers for every dotted prefix of its own short name; an anonymous enum's
8077/// or union's enumerators carry bare short names instead (#2140), which is what
8078/// an empty owner short name asks about.
8079#[derive(Debug, Default)]
8080pub struct CppFieldOwnerIndex {
8081    /// Package name -> the owner short names its fields name.
8082    owners: HashMap<String, HashSet<String>>,
8083    /// Packages holding at least one field that names no owner.
8084    ownerless_packages: HashSet<String>,
8085}
8086
8087impl CppFieldOwnerIndex {
8088    /// The index of the declarations recorded so far, built when the first
8089    /// question arrives.
8090    fn of<'unit>(
8091        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
8092        file: &ProjectFile,
8093    ) -> Self {
8094        let mut index = Self::default();
8095        for declaration in declarations {
8096            index.record(declaration, file);
8097        }
8098        index
8099    }
8100
8101    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
8102        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
8103            return;
8104        }
8105        let short_name = code_unit.short_name();
8106        let package_name = code_unit.package_name();
8107        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
8108            self.ownerless_packages.insert(package_name.to_string());
8109        }
8110        if !short_name.contains('.') {
8111            return;
8112        }
8113        if !self.owners.contains_key(package_name) {
8114            self.owners
8115                .insert(package_name.to_string(), HashSet::default());
8116        }
8117        let owners = self
8118            .owners
8119            .get_mut(package_name)
8120            .expect("the package entry was just ensured");
8121        for (offset, _) in short_name.match_indices('.') {
8122            let owner = &short_name[..offset];
8123            if !owners.contains(owner) {
8124                owners.insert(owner.to_string());
8125            }
8126        }
8127    }
8128
8129    /// Whether a field declaration in `package_name` names `owner_short_name`
8130    /// as its owner. An empty owner asks about ownerless fields instead.
8131    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
8132        if owner_short_name.is_empty() {
8133            self.ownerless_packages.contains(package_name)
8134        } else {
8135            self.owners
8136                .get(package_name)
8137                .is_some_and(|owners| owners.contains(owner_short_name))
8138        }
8139    }
8140}
8141
8142/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
8143/// debug build asserts every carried answer against and as the release-mode
8144/// parity tests' reference (#2786).
8145#[cfg(any(debug_assertions, test))]
8146fn cpp_declarations_hold_owned_fields<'unit>(
8147    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
8148    file: &ProjectFile,
8149    package_name: &str,
8150    owner_short_name: &str,
8151) -> bool {
8152    let prefix = format!("{owner_short_name}.");
8153    declarations.into_iter().any(|unit| {
8154        unit.kind() == CodeUnitType::Field
8155            && unit.source() == file
8156            && unit.package_name() == package_name
8157            && if owner_short_name.is_empty() {
8158                // Anonymous enum/union parent: its enumerators carry bare
8159                // short names (#2140), so presence means any ownerless
8160                // field in this file.
8161                !unit.short_name().contains(['.', '$'])
8162            } else {
8163                unit.short_name().starts_with(&prefix)
8164            }
8165    })
8166}
8167
8168/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
8169///
8170/// A region reparse is its own tree and is dropped while the walk that made it
8171/// continues, so a later parse can be allocated at the same address and hand out
8172/// the same node ids.  The root's span and shape pin the identity its address
8173/// alone does not: two roots agreeing on all of this are the same parse of the
8174/// same bytes, and a scan of one is a scan of the other.
8175#[derive(PartialEq, Eq, Hash)]
8176pub struct CppTreeIdentity {
8177    root_id: usize,
8178    start_byte: usize,
8179    end_byte: usize,
8180    kind_id: u16,
8181    child_count: usize,
8182}
8183
8184impl CppTreeIdentity {
8185    fn of(root: Node<'_>) -> Self {
8186        Self {
8187            root_id: root.id(),
8188            start_byte: root.start_byte(),
8189            end_byte: root.end_byte(),
8190            kind_id: root.kind_id(),
8191            child_count: root.child_count(),
8192        }
8193    }
8194}
8195
8196/// The namespace forward declarations one tree's prefix holds, folded in as the
8197/// walk asks about them.
8198///
8199/// `scope_for_recovered_exported_class` asks the same question once per
8200/// recovered class, and the answer depends only on the part of the tree that
8201/// starts before the asking node.  Rescanning that prefix per question is
8202/// quadratic in the file, and a generated header whose parse recovery leaves
8203/// thousands of class-like declarations at file scope pays all of it: 1,904
8204/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
8205/// (#2754).  This carries the scan forward instead.  Each question advances the
8206/// traversal from wherever the last one stopped to the asking node's start byte,
8207/// so a whole walk pays at most one pass over the prefix its furthest question
8208/// reaches, and each question then costs a name lookup.
8209#[derive(Default)]
8210pub struct CppNamespaceForwardScan {
8211    /// Every named node starting before this byte has been folded in.
8212    scanned_through: usize,
8213    forwards: HashMap<String, Vec<CppNamespaceForward>>,
8214}
8215
8216impl CppNamespaceForwardScan {
8217    /// Fold in every named node of `root` that starts at or after the fold
8218    /// watermark and before `cutoff`.
8219    ///
8220    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
8221    /// pass owes are exactly the ones no earlier pass reached, and a question
8222    /// about an earlier byte than one already answered costs nothing.
8223    fn advance_to<'tree>(
8224        &mut self,
8225        root: Node<'tree>,
8226        cutoff: usize,
8227        source: &str,
8228        ancestry: &ParentIndex<'tree>,
8229    ) {
8230        if cutoff <= self.scanned_through {
8231            return;
8232        }
8233        let folded_through = self.scanned_through;
8234        let mut cursor = root.walk();
8235        let mut stack = vec![root];
8236        while let Some(current) = stack.pop() {
8237            if (folded_through..cutoff).contains(&current.start_byte())
8238                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
8239            {
8240                self.forwards
8241                    .entry(forward.name.clone())
8242                    .or_default()
8243                    .push(forward);
8244            }
8245            // A subtree ending before the watermark holds only nodes an earlier
8246            // pass already folded, and one starting at or after the cutoff is
8247            // outside the prefix being asked about. Skipping both is what keeps
8248            // the total traversal to one pass.
8249            for child in current.named_children(&mut cursor) {
8250                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
8251                    stack.push(child);
8252                }
8253            }
8254        }
8255        self.scanned_through = cutoff;
8256    }
8257
8258    /// The one namespace `name` was forward declared in before `recovered_node`.
8259    /// More than one matching forward declaration is ambiguous and answers
8260    /// nothing rather than guessing.
8261    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
8262        let mut matching = self
8263            .forwards
8264            .get(name)
8265            .into_iter()
8266            .flatten()
8267            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
8268        let first = matching.next()?;
8269        matching
8270            .next()
8271            .is_none()
8272            .then(|| first.package_name.clone())
8273    }
8274}
8275
8276/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
8277/// debug build checks every answer against (and the one the parity tests drive
8278/// directly).  It walks the whole prefix per question, which is exactly the cost
8279/// #2754 removed from the release path.
8280#[cfg(any(debug_assertions, test))]
8281fn unique_earlier_cpp_namespace_forward<'tree>(
8282    recovered_node: Node<'tree>,
8283    name: &str,
8284    source: &str,
8285    ancestry: &ParentIndex<'tree>,
8286) -> Option<String> {
8287    let mut root = recovered_node;
8288    while let Some(parent) = ancestry.parent(root) {
8289        root = parent;
8290    }
8291
8292    let mut candidates = Vec::new();
8293    let mut stack = vec![root];
8294    while let Some(current) = stack.pop() {
8295        if current.start_byte() < recovered_node.start_byte()
8296            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
8297            && forward.name == name
8298            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
8299        {
8300            candidates.push(forward.package_name);
8301        }
8302
8303        let mut cursor = current.walk();
8304        for child in current.named_children(&mut cursor) {
8305            if child.start_byte() < recovered_node.start_byte() {
8306                stack.push(child);
8307            }
8308        }
8309    }
8310
8311    if candidates.len() == 1 {
8312        candidates.pop()
8313    } else {
8314        None
8315    }
8316}
8317
8318fn malformed_namespace_is_nearest_recovery_region(
8319    namespace_end_byte: usize,
8320    recovered_node: Node<'_>,
8321) -> bool {
8322    let mut root = recovered_node;
8323    while let Some(parent) = root.parent() {
8324        root = parent;
8325    }
8326    let mut cursor = root.walk();
8327    root.named_children(&mut cursor)
8328        .filter(|sibling| {
8329            namespace_end_byte <= sibling.start_byte()
8330                && sibling.end_byte() <= recovered_node.start_byte()
8331        })
8332        .all(is_malformed_namespace_recovery_trivia)
8333}
8334
8335fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
8336    matches!(node.kind(), "ERROR" | "comment")
8337        || node.kind().starts_with("preproc_")
8338        || node.kind() == "expression_statement" && node.named_child_count() == 0
8339}
8340
8341/// Return the namespace path for a forward class only when the declaration is
8342/// at namespace scope.  A declaration nested in a function/class body may share
8343/// the same namespace ancestor but cannot identify a top-level class definition.
8344fn cpp_namespace_name_for_forward<'tree>(
8345    node: Node<'tree>,
8346    source: &str,
8347    ancestry: &ParentIndex<'tree>,
8348) -> Option<String> {
8349    cpp_namespace_definition_for_forward(node, ancestry)?;
8350    cpp_lexical_namespace_name(node, source, ancestry)
8351}
8352
8353fn cpp_namespace_definition_for_forward<'tree>(
8354    node: Node<'tree>,
8355    ancestry: &ParentIndex<'tree>,
8356) -> Option<Node<'tree>> {
8357    let declaration = ancestry.parent(node)?;
8358    let mut ancestor = ancestry.parent(declaration);
8359    while let Some(current) = ancestor {
8360        if matches!(
8361            current.kind(),
8362            "compound_statement"
8363                | "field_declaration_list"
8364                | "class_specifier"
8365                | "struct_specifier"
8366                | "union_specifier"
8367                | "function_definition"
8368                | "lambda_expression"
8369        ) {
8370            return None;
8371        }
8372        if current.kind() == "namespace_definition" {
8373            return Some(current);
8374        }
8375        ancestor = ancestry.parent(current);
8376    }
8377    None
8378}
8379
8380fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
8381    match node.kind() {
8382        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
8383        "parenthesized_declarator" => node
8384            .child_by_field_name("declarator")
8385            .or_else(|| last_named_child(node))
8386            .is_some_and(is_pointer_wrapper_declarator),
8387        "template_function" => node
8388            .child_by_field_name("name")
8389            .is_some_and(is_function_pointer_like_inner_declarator),
8390        _ => false,
8391    }
8392}
8393
8394fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
8395    match node.kind() {
8396        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
8397        "parenthesized_declarator" => node
8398            .child_by_field_name("declarator")
8399            .or_else(|| last_named_child(node))
8400            .is_some_and(is_pointer_wrapper_declarator),
8401        _ => false,
8402    }
8403}
8404
8405fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
8406    let cleaned = raw_name.trim_start_matches("template ").trim();
8407    // A leading `::` is the explicit-global marker, not an empty owner segment.
8408    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
8409    // erroneous macro envelope swallowing the first identifier of an
8410    // out-of-line `X::X` constructor, chromium #1573); without this strip the
8411    // split below yields owner_parts `[""]`, constructing a unit with an empty
8412    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
8413    let cleaned = cleaned.trim_start_matches("::");
8414    // Parser recovery can preserve two adjacent scope operators around a
8415    // missing component (for example `X::/**/::method` in compiler diagnostic
8416    // fixtures). Empty components are syntax-recovery artifacts, never C++
8417    // owners. Keeping one as the final owner constructed `short_name=".method"`
8418    // and violated the structured package/short boundary during a large LLVM
8419    // workspace build. This is the same legacy-string-to-FqName bridge as the
8420    // ordinary split above; discard only components that the delimiter itself
8421    // proves empty.
8422    let parts: Vec<_> = cleaned
8423        .split("::")
8424        .filter(|component| !component.is_empty())
8425        .collect();
8426    if parts.is_empty() {
8427        return (None, cleaned.to_string(), scope.package_name.clone());
8428    }
8429    if parts.len() > 1 {
8430        let name = parts.last().unwrap_or(&cleaned).to_string();
8431        let owner_parts = &parts[..parts.len() - 1];
8432        if let Some(class_unit) = &scope.class_unit {
8433            // Lexically inside a class body: the owner is that class, whatever
8434            // the declarator re-qualifies it as.
8435            return (
8436                Some(CppMemberOwner::Unit(class_unit.clone())),
8437                name,
8438                scope.package_name.clone(),
8439            );
8440        }
8441        if !scope.package_name.is_empty() {
8442            // Out-of-line member definition written *inside* an enclosing
8443            // `namespace {}` block (scope package is that namespace). Every
8444            // owner segment before the terminal member is a class-nesting step
8445            // -- an out-of-line nested-class member `Outer::Inner::method` in
8446            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
8447            // namespace path: `using namespace` never brings nested-class
8448            // access into unqualified scope, so C++ always writes the full
8449            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
8450            // that redundantly re-states the enclosing namespace it already
8451            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
8452            // strip that re-qualifying prefix (which duplicates a suffix of the
8453            // enclosing package path) before treating what remains as the
8454            // nested-class chain, so the redundant spelling still lands on the
8455            // same `log4cxx.Foo.method` identity as its header declaration.
8456            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
8457            let owner = (!nested.is_empty()).then(|| {
8458                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
8459            });
8460            return (owner, name, scope.package_name.clone());
8461        }
8462        // File scope (no enclosing `namespace {}` block, scope package empty).
8463        let (owner, package_name) = if owner_parts.len() > 1 {
8464            // A multi-segment qualifier at file scope with no enclosing
8465            // namespace: treat all but the last owner segment as the namespace
8466            // path and the last as the owning class (`ns1::ns2::Class::method`
8467            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
8468            // is really a namespace or an outer class cannot be told from the
8469            // declarator text alone here, and no enclosing namespace or
8470            // in-index owner is available at per-file extraction to confirm the
8471            // class reading, so the far-more-common namespace interpretation is
8472            // kept rather than guessed away (the nested-class-at-file-scope and
8473            // using-directive-qualified nested-class shapes remain on this
8474            // behavior; see #1121).
8475            (
8476                Some(CppMemberOwner::Chain(vec![
8477                    owner_parts.last().unwrap_or(&"").to_string(),
8478                ])),
8479                owner_parts[..owner_parts.len() - 1].join("::"),
8480            )
8481        } else {
8482            // A bare `Class::member` qualifier at file scope carries no
8483            // namespace segment of its own. The declarator alone cannot say
8484            // which namespace owns `Class` -- but a `using namespace X;`
8485            // directive already in effect at this point in the file (#1093,
8486            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
8487            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
8488            // is the remaining structural signal for it, so fall back to it
8489            // rather than leaving the definition's package empty while its
8490            // header declaration (parsed inside the `namespace {}` block) keeps
8491            // the real one -- an identity split that made the same member
8492            // unresolvable under its own displayed spelling.
8493            (
8494                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
8495                cpp_using_directive_namespace_for_bare_owner(scope),
8496            )
8497        };
8498        return (owner, name, package_name);
8499    }
8500
8501    let package_name = scope.package_name.clone();
8502    let owner = scope
8503        .class_unit
8504        .as_ref()
8505        .map(|parent| CppMemberOwner::Unit(parent.clone()));
8506    (owner, cleaned.to_string(), package_name)
8507}
8508
8509/// Drop the leading owner segments of an out-of-line member qualifier that
8510/// merely re-state the enclosing namespace the definition already sits in, so
8511/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
8512/// definition may redundantly write `a::b::Outer::Inner::method` (or the
8513/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
8514/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
8515/// noise, not class-nesting steps. Returns the owner segments with the longest
8516/// such re-qualifying prefix removed (possibly all of them, when the qualifier
8517/// names only the enclosing namespace before the terminal member -- a
8518/// re-qualified free function). `package_name` is the enclosing namespace path
8519/// in its stored `::`-joined form; both sides are split on the same delimiter
8520/// the namespace walker joined them with, so this compares namespace *segments*
8521/// rather than scanning text.
8522fn strip_redundant_namespace_prefix<'a>(
8523    owner_parts: &'a [&'a str],
8524    package_name: &str,
8525) -> &'a [&'a str] {
8526    if package_name.is_empty() {
8527        return owner_parts;
8528    }
8529    let package_segments: Vec<&str> = package_name.split("::").collect();
8530    let max_prefix = owner_parts.len().min(package_segments.len());
8531    for prefix_len in (1..=max_prefix).rev() {
8532        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
8533        if &owner_parts[..prefix_len] == package_suffix {
8534            return &owner_parts[prefix_len..];
8535        }
8536    }
8537    owner_parts
8538}
8539
8540/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
8541/// class name at file/namespace scope, from the `using namespace` directives
8542/// visible at this point in the file. Several may be in scope at once (a
8543/// primary `using namespace NS;` alongside deeper conveniences like `using
8544/// namespace NS::helpers;`); since the declarator gives no way to tell which
8545/// one actually declares the owner class, prefer the shallowest (fewest
8546/// `::`-separated segments) as the file's most likely "home" namespace,
8547/// breaking ties by declaration order. Returns an empty string (leaving the
8548/// caller's package unqualified, as before) when no using-namespace directive
8549/// is in scope.
8550fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
8551    scope
8552        .visible_using_namespaces
8553        .iter()
8554        .min_by_key(|namespace| namespace.split("::").count())
8555        .cloned()
8556        .unwrap_or_default()
8557}
8558
8559struct CppQualifiedNameComponent {
8560    name: String,
8561    is_template_id: bool,
8562}
8563
8564/// Canonical nested-class chain for an out-of-line class definition written
8565/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
8566/// component per class (`["Outer", "Inner"]`).
8567///
8568/// The enclosing namespace fixes the namespace/class boundary: after an
8569/// optional redundant spelling of that namespace, every component belongs to
8570/// the class chain. File-scope qualified class names remain untouched because
8571/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
8572///
8573/// The components stay structured (rather than being `$`-joined here) so the
8574/// fq construction can push one Type/Nested segment per class; the `$`-joined
8575/// short-name display form is derived at the call sites that need it.
8576fn qualified_class_name_chain(
8577    class_node: Node<'_>,
8578    source: &str,
8579    scope: &ScopeInfo,
8580) -> Option<Vec<String>> {
8581    if scope.package_name.is_empty() || scope.class_unit.is_some() {
8582        return None;
8583    }
8584    let name = class_node.child_by_field_name("name")?;
8585    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
8586    if explicitly_global
8587        || components.len() < 2
8588        || components.iter().any(|component| component.is_template_id)
8589    {
8590        return None;
8591    }
8592    let names = components
8593        .iter()
8594        .map(|component| component.name.as_str())
8595        .collect::<Vec<_>>();
8596    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
8597    if class_chain.is_empty() {
8598        return None;
8599    }
8600    Some(class_chain.iter().map(|name| name.to_string()).collect())
8601}
8602
8603fn structured_cpp_qualified_components(
8604    qualified_name: Node<'_>,
8605    source: &str,
8606) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
8607    if qualified_name.kind() != "qualified_identifier" {
8608        return None;
8609    }
8610
8611    let mut components = Vec::new();
8612    let mut current = qualified_name;
8613    let mut explicitly_global = false;
8614    loop {
8615        if current.kind() == "qualified_identifier" {
8616            if let Some(component) = current.child_by_field_name("scope") {
8617                components.push(canonical_cpp_qualified_component(component, source)?);
8618            } else if components.is_empty() {
8619                explicitly_global = true;
8620            } else {
8621                return None;
8622            }
8623            current = current.child_by_field_name("name")?;
8624        } else {
8625            components.push(canonical_cpp_qualified_component(current, source)?);
8626            break;
8627        }
8628    }
8629    Some((components, explicitly_global))
8630}
8631
8632fn split_structured_templated_cpp_name(
8633    declarator_name: Node<'_>,
8634    source: &str,
8635    scope: &ScopeInfo,
8636) -> Option<(Option<CppMemberOwner>, String, String)> {
8637    let (mut components, explicitly_global) =
8638        structured_cpp_qualified_components(declarator_name, source)?;
8639
8640    let terminal = components.pop()?;
8641    let owner_start = components
8642        .iter()
8643        .position(|component| component.is_template_id)?;
8644    let explicit_package = components[..owner_start]
8645        .iter()
8646        .map(|component| component.name.as_str())
8647        .collect::<Vec<_>>()
8648        .join("::");
8649    let explicit_package_is_empty = explicit_package.is_empty();
8650    let package_name = match (
8651        explicitly_global,
8652        scope.package_name.is_empty(),
8653        explicit_package_is_empty,
8654    ) {
8655        (true, _, _) => explicit_package,
8656        (false, _, true) => scope.package_name.clone(),
8657        (false, true, false) => explicit_package,
8658        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
8659    };
8660    // Same identity-split fallback as `split_cpp_name` (#1093): a template
8661    // specialization's owner class named with no namespace segment of its own
8662    // (`explicit_package` empty) at file scope (`explicitly_global` false)
8663    // with nothing enclosing (`package_name` still empty) has no structural
8664    // signal for its namespace besides an in-scope `using namespace X;`.
8665    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
8666    {
8667        cpp_using_directive_namespace_for_bare_owner(scope)
8668    } else {
8669        package_name
8670    };
8671    let owner_chain = components[owner_start..]
8672        .iter()
8673        .map(|component| component.name.clone())
8674        .collect::<Vec<_>>();
8675    if owner_chain.is_empty() || terminal.name.is_empty() {
8676        return None;
8677    }
8678
8679    Some((
8680        Some(CppMemberOwner::Chain(owner_chain)),
8681        terminal.name,
8682        package_name,
8683    ))
8684}
8685
8686fn canonical_cpp_qualified_component(
8687    mut component: Node<'_>,
8688    source: &str,
8689) -> Option<CppQualifiedNameComponent> {
8690    let mut is_template_id = false;
8691    loop {
8692        match component.kind() {
8693            "template_type" => {
8694                is_template_id = true;
8695                component = component.child_by_field_name("name")?;
8696            }
8697            "dependent_name" => component = component.named_child(0)?,
8698            "identifier"
8699            | "field_identifier"
8700            | "namespace_identifier"
8701            | "type_identifier"
8702            | "operator_name"
8703            | "destructor_name" => {
8704                let name = normalize_cpp_whitespace(node_text(component, source));
8705                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
8706                    name,
8707                    is_template_id,
8708                });
8709            }
8710            _ => component = component.child_by_field_name("name")?,
8711        }
8712    }
8713}
8714
8715fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
8716    if let Some(name) = macro_decorated_unqualified_name(node) {
8717        return extract_declarator_name(name, source);
8718    }
8719    match node.kind() {
8720        "identifier"
8721        | "field_identifier"
8722        | "type_identifier"
8723        | "operator_name"
8724        | "destructor_name"
8725        | "qualified_identifier" => node_text(node, source).to_string(),
8726        "function_declarator"
8727        | "pointer_declarator"
8728        | "reference_declarator"
8729        | "parenthesized_declarator"
8730        | "array_declarator"
8731        | "template_function" => node
8732            .child_by_field_name("declarator")
8733            .or_else(|| node.child_by_field_name("name"))
8734            .or_else(|| last_named_child(node))
8735            .map(|child| extract_declarator_name(child, source))
8736            .unwrap_or_else(|| node_text(node, source).to_string()),
8737        _ => node
8738            .child_by_field_name("name")
8739            .map(|child| extract_declarator_name(child, source))
8740            .unwrap_or_else(|| node_text(node, source).to_string()),
8741    }
8742}
8743
8744/// Extract a callable identity only through declaration-shaped AST nodes.
8745/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
8746/// expose the call's parameter list as a false function declarator; accepting
8747/// arbitrary node text there emitted bogus names such as `.*f`.
8748fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
8749    if let Some(name) = macro_decorated_unqualified_name(node) {
8750        return extract_callable_declarator_name(name, source);
8751    }
8752    match node.kind() {
8753        "identifier"
8754        | "field_identifier"
8755        | "type_identifier"
8756        | "operator_name"
8757        | "destructor_name"
8758        | "qualified_identifier" => Some(node_text(node, source).to_string()),
8759        "function_declarator"
8760        | "pointer_declarator"
8761        | "reference_declarator"
8762        | "parenthesized_declarator"
8763        | "array_declarator"
8764        | "template_function" => node
8765            .child_by_field_name("declarator")
8766            .or_else(|| node.child_by_field_name("name"))
8767            .and_then(|child| extract_callable_declarator_name(child, source)),
8768        _ => None,
8769    }
8770}
8771
8772fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
8773    match node.kind() {
8774        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
8775            let name = node_text(node, source).trim().to_string();
8776            (!name.is_empty()).then_some(name)
8777        }
8778        _ => node
8779            .child_by_field_name("declarator")
8780            .or_else(|| node.child_by_field_name("name"))
8781            .or_else(|| last_named_child(node))
8782            .and_then(|child| extract_variable_name(child, source)),
8783    }
8784}
8785
8786/// Recover a C field whose name is wrapped in the function-like
8787/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
8788/// function declarator, while retaining its argument as a structured child.
8789/// The exact macro name and one-argument shape keep ordinary function-pointer
8790/// fields and unknown malformed declarators fail-closed.
8791#[derive(Clone, Copy)]
8792pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
8793    pub(crate) name: Node<'tree>,
8794    pub(crate) declarator: Node<'tree>,
8795}
8796
8797impl RecoveredFunctionLikeFieldDeclarator<'_> {
8798    pub(crate) fn pointer_depth(self) -> i32 {
8799        let mut depth = 0;
8800        let mut current = self.declarator;
8801        while current.kind() != "function_declarator" {
8802            if current.kind() == "pointer_declarator" {
8803                depth += 1;
8804            }
8805            current = current
8806                .child_by_field_name("declarator")
8807                .expect("recovered field wrapper has an inner declarator");
8808        }
8809        depth
8810    }
8811}
8812
8813pub(crate) fn recovered_function_like_field_declarator<'tree>(
8814    node: Node<'tree>,
8815    source: &str,
8816) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
8817    if node.kind() != "field_declaration" {
8818        return None;
8819    }
8820    let outer_declarator = node.child_by_field_name("declarator")?;
8821    let mut declarator = outer_declarator;
8822    while matches!(
8823        declarator.kind(),
8824        "pointer_declarator"
8825            | "reference_declarator"
8826            | "array_declarator"
8827            | "parenthesized_declarator"
8828    ) {
8829        declarator = declarator.child_by_field_name("declarator")?;
8830    }
8831    if declarator.kind() != "function_declarator" {
8832        return None;
8833    }
8834    let macro_name = declarator.child_by_field_name("declarator")?;
8835    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
8836    {
8837        return None;
8838    }
8839    let parameters = declarator.child_by_field_name("parameters")?;
8840    let mut cursor = parameters.walk();
8841    let mut arguments = parameters.named_children(&mut cursor);
8842    let parameter = arguments.next()?;
8843    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
8844        return None;
8845    }
8846    let name = parameter.child_by_field_name("type").filter(|argument| {
8847        matches!(
8848            argument.kind(),
8849            "identifier" | "field_identifier" | "type_identifier"
8850        )
8851    })?;
8852    Some(RecoveredFunctionLikeFieldDeclarator {
8853        name,
8854        declarator: outer_declarator,
8855    })
8856}
8857
8858fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
8859    let count = node.named_child_count();
8860    if count == 0 {
8861        None
8862    } else {
8863        node.named_child(count - 1)
8864    }
8865}
8866
8867fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
8868    let name_node = node.child_by_field_name("name")?;
8869    let name = normalize_cpp_whitespace(node_text(name_node, source));
8870    (!name.is_empty()).then_some(name)
8871}
8872
8873fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
8874    if node.kind() != "declaration" {
8875        return Vec::new();
8876    }
8877    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
8878        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
8879    }) else {
8880        return Vec::new();
8881    };
8882    let Some(declarator) = node.child_by_field_name("declarator") else {
8883        return Vec::new();
8884    };
8885    if node_text(keyword, source) == "using"
8886        && (declarator.kind() != "init_declarator"
8887            || declarator.child_by_field_name("value").is_none())
8888    {
8889        return Vec::new();
8890    }
8891    if node_text(keyword, source) == "typedef"
8892        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
8893    {
8894        return vec![alias_name];
8895    }
8896    extract_typedef_declarator_name(declarator, source)
8897        .into_iter()
8898        .collect()
8899}
8900
8901fn recovered_typedef_error_alias_name(
8902    declaration: Node<'_>,
8903    declarator: Node<'_>,
8904    source: &str,
8905) -> Option<String> {
8906    // An export macro between `class` and its name can make tree-sitter parse
8907    // the recovered class body as a function body. In that shape,
8908    //
8909    //     typedef spi::Filter BASE_CLASS;
8910    //
8911    // becomes a declaration whose `declarator` is the underlying qualified
8912    // type (`spi::Filter`) and whose actual alias name is displaced into the
8913    // following ERROR node. Do not publish the terminal underlying type
8914    // (`Filter`) as a false class-owned alias.
8915    if declarator.kind() != "qualified_identifier" {
8916        return None;
8917    }
8918    let mut cursor = declaration.walk();
8919    let mut errors = declaration
8920        .named_children(&mut cursor)
8921        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
8922    let error = errors.next()?;
8923    if errors.next().is_some() || error.named_child_count() != 1 {
8924        return None;
8925    }
8926    let name = error.named_child(0)?;
8927    if !matches!(
8928        name.kind(),
8929        "identifier" | "field_identifier" | "type_identifier"
8930    ) {
8931        return None;
8932    }
8933    let name = normalize_cpp_whitespace(node_text(name, source));
8934    (!name.is_empty()).then_some(name)
8935}
8936
8937fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
8938    // A function-like token in the type position can make tree-sitter expose
8939    // its argument as a parenthesized declarator. Do not publish that argument
8940    // as an alias. The macro-specific recovery below handles the proven shape.
8941    if fragmented_parenthesized_typedef_type(node).is_some() {
8942        return Vec::new();
8943    }
8944    let has_function_like_macro_type = node
8945        .child_by_field_name("type")
8946        .filter(|type_node| type_node.kind() == "type_identifier")
8947        .is_some_and(|type_node| {
8948            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
8949        });
8950    let mut names = Vec::new();
8951    let mut cursor = node.walk();
8952    for declarator in node.children_by_field_name("declarator", &mut cursor) {
8953        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
8954            continue;
8955        }
8956        if let Some(name) = extract_typedef_declarator_name(declarator, source)
8957            && !names.contains(&name)
8958        {
8959            names.push(name);
8960        }
8961    }
8962    names
8963}
8964
8965struct RecoveredMacroTypedefAlias<'tree> {
8966    name: String,
8967    end_node: Node<'tree>,
8968}
8969
8970/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
8971/// into an identifier expression statement. The uppercase macro token, missing
8972/// typedef terminator, and complete sibling terminator prove this exact shape.
8973fn recovered_macro_typedef_alias<'tree>(
8974    node: Node<'tree>,
8975    source: &str,
8976) -> Option<RecoveredMacroTypedefAlias<'tree>> {
8977    let type_node = fragmented_parenthesized_typedef_type(node)?;
8978    if type_node.kind() != "type_identifier"
8979        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
8980    {
8981        return None;
8982    }
8983
8984    let end_node = node.next_named_sibling()?;
8985    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
8986        return None;
8987    }
8988    let name_node = end_node.named_child(0)?;
8989    if name_node.kind() != "identifier" {
8990        return None;
8991    }
8992    let has_terminator = (0..end_node.child_count()).any(|index| {
8993        end_node
8994            .child(index)
8995            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
8996    });
8997    if !has_terminator {
8998        return None;
8999    }
9000    let name = normalize_cpp_whitespace(node_text(name_node, source));
9001    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
9002}
9003
9004fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
9005    if node.kind() != "type_definition" {
9006        return None;
9007    }
9008    let mut declarator_cursor = node.walk();
9009    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
9010    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
9011        return None;
9012    }
9013    let has_missing_terminator = (0..node.child_count()).any(|index| {
9014        node.child(index)
9015            .is_some_and(|child| child.kind() == ";" && child.is_missing())
9016    });
9017    if !has_missing_terminator {
9018        return None;
9019    }
9020    node.child_by_field_name("type")
9021}
9022
9023fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9024    match node.kind() {
9025        "identifier" | "field_identifier" | "type_identifier" => {
9026            let name = normalize_cpp_whitespace(node_text(node, source));
9027            (!name.is_empty()).then_some(name)
9028        }
9029        "qualified_identifier" => node
9030            .child_by_field_name("name")
9031            .and_then(|name| extract_typedef_declarator_name(name, source)),
9032        _ => node
9033            .child_by_field_name("declarator")
9034            .or_else(|| node.child_by_field_name("name"))
9035            .or_else(|| last_named_child(node))
9036            .and_then(|child| extract_typedef_declarator_name(child, source)),
9037    }
9038}
9039
9040fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
9041    let name = node
9042        .child_by_field_name("name")
9043        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
9044        .or_else(|| {
9045            let mut cursor = node.walk();
9046            node.named_children(&mut cursor)
9047                .find(|child| {
9048                    matches!(
9049                        child.kind(),
9050                        "identifier" | "field_identifier" | "type_identifier"
9051                    )
9052                })
9053                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
9054        })?;
9055    (!name.is_empty()).then_some(name)
9056}
9057
9058fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
9059    left.id() == right.id()
9060}
9061
9062fn render_cpp_type_signature(
9063    node: Node<'_>,
9064    source: &str,
9065    template_signature: Option<&str>,
9066) -> String {
9067    let text = normalize_cpp_whitespace(node_text(node, source));
9068    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
9069    let rendered = if head.ends_with(';') {
9070        head.to_string()
9071    } else {
9072        format!("{head} {{")
9073    };
9074    if let Some(template_signature) = template_signature {
9075        format!("template {template_signature} {rendered}")
9076    } else {
9077        rendered
9078    }
9079}
9080
9081fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
9082    if let Some(recovered) = recovered_pyobject_head_field(node, source)
9083        && recovered.declarator == declarator
9084    {
9085        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
9086        let name = normalize_cpp_whitespace(node_text(recovered.declarator, source));
9087        return format!("{type_text} {name};");
9088    }
9089    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
9090        && recovered.name == declarator
9091    {
9092        let type_text = node
9093            .child_by_field_name("type")
9094            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
9095            .unwrap_or_default();
9096        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
9097        let mut prefix = String::new();
9098        let mut suffix = String::new();
9099        let mut current = recovered.declarator;
9100        while current.kind() != "function_declarator" {
9101            match current.kind() {
9102                "pointer_declarator" => prefix.push('*'),
9103                "reference_declarator" => prefix.push('&'),
9104                "array_declarator" => {
9105                    let size = current
9106                        .child_by_field_name("size")
9107                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
9108                        .unwrap_or_default();
9109                    suffix.push('[');
9110                    suffix.push_str(&size);
9111                    suffix.push(']');
9112                }
9113                "parenthesized_declarator" => {}
9114                _ => unreachable!("validated recovered field declarator wrapper"),
9115            }
9116            current = current
9117                .child_by_field_name("declarator")
9118                .expect("recovered field wrapper has an inner declarator");
9119        }
9120        let separator = if prefix.is_empty() { "" } else { " " };
9121        return format!("{type_text} {prefix}{separator}{name}{suffix};");
9122    }
9123    if let Some(signature) =
9124        render_recovered_macro_qualified_field_signature(node, declarator, source)
9125    {
9126        return signature;
9127    }
9128    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
9129    let prefix = cpp_declaration_prefix(node, source);
9130    let name = extract_variable_name(declarator, source).unwrap_or_default();
9131    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
9132    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
9133        || (prefix.ends_with('&') && raw_suffix == "&")
9134    {
9135        String::new()
9136    } else {
9137        raw_suffix
9138    };
9139
9140    let mut rendered = if suffix.is_empty() {
9141        format!("{prefix} {name}")
9142    } else if suffix.starts_with('*') || suffix.starts_with('&') {
9143        format!("{prefix}{suffix} {name}")
9144    } else if suffix.starts_with('[') || suffix.starts_with('(') {
9145        format!("{prefix} {name}{suffix}")
9146    } else {
9147        format!("{prefix} {suffix}{name}")
9148    };
9149    rendered = collapse_cpp_whitespace(&rendered);
9150
9151    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
9152        format!("{rendered} = {initializer};")
9153    } else if declaration_text.ends_with(';') {
9154        format!("{rendered};")
9155    } else {
9156        rendered
9157    }
9158}
9159
9160fn render_recovered_macro_qualified_field_signature(
9161    node: Node<'_>,
9162    declarator: Node<'_>,
9163    source: &str,
9164) -> Option<String> {
9165    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
9166    if !recovered
9167        .iter()
9168        .any(|candidate| same_node(*candidate, declarator))
9169    {
9170        return None;
9171    }
9172    let pseudo_declarator = node.child_by_field_name("declarator")?;
9173    let mut cursor = node.walk();
9174    let clause = node
9175        .named_children(&mut cursor)
9176        .find(|child| child.kind() == "bitfield_clause")?;
9177    let mut cursor = clause.walk();
9178    let error = clause
9179        .named_children(&mut cursor)
9180        .find(|child| child.kind() == "ERROR")?;
9181    let qualified_type =
9182        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
9183    let prefix = cpp_declaration_prefix(node, source);
9184    let name = extract_variable_name(declarator, source)?;
9185    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
9186    let mut rendered = if suffix.is_empty() {
9187        format!("{prefix} {qualified_type} {name}")
9188    } else {
9189        format!("{prefix} {qualified_type} {suffix} {name}")
9190    };
9191    rendered = collapse_cpp_whitespace(&rendered);
9192
9193    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
9194        Some(format!(
9195            "{rendered} = {};",
9196            normalize_cpp_whitespace(node_text(initializer, source))
9197        ))
9198    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
9199        Some(format!("{rendered} = {initializer};"))
9200    } else {
9201        Some(format!("{rendered};"))
9202    }
9203}
9204
9205fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
9206    match node.kind() {
9207        "pointer_expression" => {
9208            let operator = node
9209                .child_by_field_name("operator")
9210                .or_else(|| node.child(0))
9211                .map(|operator| node_text(operator, source))
9212                .unwrap_or("*");
9213            let argument = node
9214                .child_by_field_name("argument")
9215                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
9216                .unwrap_or_default();
9217            format!("{operator}{argument}")
9218        }
9219        "unary_expression" => {
9220            let operator = node
9221                .child_by_field_name("operator")
9222                .or_else(|| node.child(0))
9223                .map(|operator| node_text(operator, source))
9224                .unwrap_or_default();
9225            let argument = node
9226                .child_by_field_name("argument")
9227                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
9228                .unwrap_or_default();
9229            format!("{operator}{argument}")
9230        }
9231        "identifier" | "field_identifier" => String::new(),
9232        _ => cpp_declarator_suffix_without_name(node, source),
9233    }
9234}
9235
9236fn recovered_macro_qualified_field_initializer<'tree>(
9237    clause: Node<'tree>,
9238    declarator: Node<'tree>,
9239) -> Option<Node<'tree>> {
9240    let mut stack = vec![clause];
9241    while let Some(current) = stack.pop() {
9242        if current.kind() == "assignment_expression"
9243            && current
9244                .child_by_field_name("left")
9245                .is_some_and(|left| same_node(left, declarator))
9246        {
9247            return current.child_by_field_name("right");
9248        }
9249        let mut cursor = current.walk();
9250        stack.extend(current.named_children(&mut cursor));
9251    }
9252    None
9253}
9254
9255fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
9256    let text = node_text(node, source);
9257    let mut cursor = node.walk();
9258    let first_declarator = node.named_children(&mut cursor).find(|child| {
9259        matches!(
9260            child.kind(),
9261            "init_declarator"
9262                | "identifier"
9263                | "field_identifier"
9264                | "pointer_declarator"
9265                | "reference_declarator"
9266                | "array_declarator"
9267                | "function_declarator"
9268        )
9269    });
9270    let prefix = if let Some(first_declarator) = first_declarator {
9271        let end = first_declarator
9272            .start_byte()
9273            .saturating_sub(node.start_byte());
9274        let mut prefix = text.get(..end).unwrap_or(text).to_string();
9275        let declarator_suffix = match first_declarator.kind() {
9276            "init_declarator" => first_declarator
9277                .child_by_field_name("declarator")
9278                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
9279                .unwrap_or_default(),
9280            _ => cpp_declarator_suffix_without_name(first_declarator, source),
9281        };
9282        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
9283            prefix.push_str(&declarator_suffix);
9284        }
9285        return collapse_cpp_whitespace(&prefix)
9286            .trim_end_matches(',')
9287            .trim_end_matches(';')
9288            .trim()
9289            .to_string();
9290    } else {
9291        text
9292    };
9293    collapse_cpp_whitespace(prefix)
9294        .trim_end_matches(',')
9295        .trim_end_matches(';')
9296        .trim()
9297        .to_string()
9298}
9299
9300fn cpp_preserved_initializer(
9301    declaration_node: Node<'_>,
9302    declarator: Node<'_>,
9303    source: &str,
9304) -> Option<String> {
9305    let name = extract_variable_name(declarator, source)?;
9306    let mut cursor = declaration_node.walk();
9307    for child in declaration_node.named_children(&mut cursor) {
9308        if child.kind() != "init_declarator" {
9309            continue;
9310        }
9311        let Some(inner) = child.child_by_field_name("declarator") else {
9312            continue;
9313        };
9314        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
9315            continue;
9316        }
9317        let value = child.child_by_field_name("value")?;
9318        let kind = value.kind();
9319        if matches!(
9320            kind,
9321            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
9322        ) {
9323            return Some(normalize_cpp_whitespace(node_text(value, source)));
9324        }
9325        break;
9326    }
9327    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
9328    let pattern = format!(
9329        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
9330        regex::escape(&name)
9331    );
9332    Regex::new(&pattern)
9333        .ok()
9334        .and_then(|regex| regex.captures(&declaration_text))
9335        .and_then(|captures| captures.get(1))
9336        .map(|value| value.as_str().to_string())
9337}
9338
9339fn render_cpp_function_display_signature_from_node<'tree>(
9340    node: Node<'tree>,
9341    source: &str,
9342    template_signature: Option<&str>,
9343    has_body: bool,
9344    ancestry: &ParentIndex<'tree>,
9345) -> String {
9346    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
9347    let parent_text = node_text(root, source);
9348    let body_local_start = root
9349        .child_by_field_name("body")
9350        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
9351        .unwrap_or(parent_text.len());
9352    let display = parent_text
9353        .get(..body_local_start)
9354        .unwrap_or(parent_text)
9355        .trim()
9356        .trim();
9357    let display = if let Some(template_signature) = template_signature {
9358        if display.starts_with("template ") {
9359            display.to_string()
9360        } else {
9361            format!("template {template_signature} {display}")
9362        }
9363    } else {
9364        display.to_string()
9365    };
9366    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
9367    if has_body {
9368        format!("{display} {{...}}")
9369    } else {
9370        format!("{display};")
9371    }
9372}
9373
9374fn cpp_template_signature(
9375    template_node: Node<'_>,
9376    declaration_child: Node<'_>,
9377    source: &str,
9378) -> Option<String> {
9379    let text = source
9380        .get(template_node.start_byte()..declaration_child.start_byte())
9381        .unwrap_or("");
9382    let text = normalize_cpp_whitespace(text);
9383    let start = text.find('<')?;
9384    let end = text.rfind('>')?;
9385    if end < start {
9386        return None;
9387    }
9388    Some(text[start..=end].to_string())
9389}
9390
9391struct RecoveredFragmentedPartialSpecialization<'tree> {
9392    declaration_node: Node<'tree>,
9393    name: String,
9394    range: Range,
9395    prefix_members: Vec<Node<'tree>>,
9396    member_siblings: Vec<Node<'tree>>,
9397    following_declarations: Vec<Node<'tree>>,
9398}
9399
9400struct RecoveredFragmentedPreprocessorClass<'tree> {
9401    declaration_node: Node<'tree>,
9402    class_node: Node<'tree>,
9403    body: Node<'tree>,
9404    name: String,
9405    range: Range,
9406    tail_members: Vec<Node<'tree>>,
9407    member_siblings: Vec<Node<'tree>>,
9408}
9409
9410/// Recover a class whose preprocessor-fragmented parse closes at an early
9411/// member body and publishes the remaining in-class declarations as siblings
9412/// of the surrounding alternative. Primary classes are admitted only when an
9413/// earlier branch contains the matching bodyless declaration and the class
9414/// node retains the displaced `#endif`. Partial specializations instead carry
9415/// their identity structurally in the `template_type` name and template
9416/// metadata. Retain the original AST nodes and re-own only the siblings through
9417/// the displaced structural `};` terminator.
9418fn recover_fragmented_preprocessor_class<'tree>(
9419    template_node: Node<'tree>,
9420    source: &str,
9421    ancestry: &ParentIndex<'tree>,
9422) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
9423    let alternative = ancestry.parent(template_node)?;
9424    if alternative.kind() != "preproc_else" {
9425        return None;
9426    }
9427    let conditional = alternative.parent()?;
9428    if conditional.kind() != "preproc_if" {
9429        return None;
9430    }
9431    let declaration_node = template_node
9432        .named_children(&mut template_node.walk())
9433        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
9434    let class_node = declaration_node
9435        .named_children(&mut declaration_node.walk())
9436        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
9437    let body = cpp_body_node(class_node)?;
9438    if class_node.end_byte() >= declaration_node.end_byte() {
9439        return None;
9440    }
9441    let name = class_like_name(class_node, source, ancestry)?;
9442    let is_partial_specialization = class_node
9443        .child_by_field_name("name")
9444        .is_some_and(|class_name| class_name.kind() == "template_type");
9445    if is_partial_specialization {
9446        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
9447        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
9448            return None;
9449        }
9450    } else {
9451        if !class_has_displaced_preprocessor_terminator(class_node) {
9452            return None;
9453        }
9454        let matching_other_branch = conditional
9455            .named_children(&mut conditional.walk())
9456            .take_while(|child| !same_node(*child, alternative))
9457            .filter(|child| child.kind() == "template_declaration")
9458            .filter_map(first_class_like_child)
9459            .any(|candidate| {
9460                cpp_body_node(candidate).is_none()
9461                    && class_like_name(candidate, source, ancestry).as_deref()
9462                        == Some(name.as_str())
9463            });
9464        if !matching_other_branch {
9465            return None;
9466        }
9467    }
9468
9469    let mut tail_members = Vec::new();
9470    let mut saw_class = false;
9471    let mut declaration_cursor = declaration_node.walk();
9472    for child in declaration_node.named_children(&mut declaration_cursor) {
9473        if same_node(child, class_node) {
9474            saw_class = true;
9475        } else if saw_class {
9476            tail_members.push(child);
9477        }
9478    }
9479
9480    let mut member_siblings = Vec::new();
9481    let mut saw_template = false;
9482    let mut terminator = None;
9483    for index in 0..alternative.child_count() {
9484        let Some(child) = alternative.child(index) else {
9485            continue;
9486        };
9487        if same_node(child, template_node) {
9488            saw_template = true;
9489            continue;
9490        }
9491        if !saw_template {
9492            continue;
9493        }
9494        if displaced_fragmented_class_terminator(alternative, index) {
9495            terminator = alternative.child(index + 1);
9496            break;
9497        }
9498        if child.is_named() {
9499            member_siblings.push(child);
9500        }
9501    }
9502    let terminator = terminator?;
9503    Some(RecoveredFragmentedPreprocessorClass {
9504        declaration_node,
9505        class_node,
9506        body,
9507        name,
9508        range: Range {
9509            start_byte: class_node.start_byte(),
9510            end_byte: terminator.end_byte(),
9511            start_line: class_node.start_position().row + 1,
9512            end_line: terminator.end_position().row + 1,
9513        },
9514        tail_members,
9515        member_siblings,
9516    })
9517}
9518
9519fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
9520    (0..class_node.child_count()).any(|index| {
9521        class_node.child(index).is_some_and(|child| {
9522            child.kind() == "ERROR"
9523                && (0..child.child_count()).any(|error_index| {
9524                    child
9525                        .child(error_index)
9526                        .is_some_and(|token| token.kind() == "#endif")
9527                })
9528        })
9529    })
9530}
9531
9532/// The real `#endif` that tree-sitter consumed inside an error subtree.
9533///
9534/// A preprocessor directive inside a malformed array bound can cause later
9535/// declarations to remain children of the conditional. The non-missing token
9536/// still gives the exact structured boundary. Ignore nested conditionals and
9537/// select the last error-owned token. Tree-sitter can pair a later outer
9538/// `#endif` with this conditional, so the direct terminator is not necessarily
9539/// missing.
9540pub fn cpp_displaced_preprocessor_terminator<'tree>(
9541    conditional: Node<'tree>,
9542) -> Option<Node<'tree>> {
9543    if !conditional.has_error() {
9544        return None;
9545    }
9546    let has_concrete_direct_terminator = conditional
9547        .child_count()
9548        .checked_sub(1)
9549        .and_then(|index| conditional.child(index))
9550        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
9551    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
9552        // A structured alternative proves that the direct `#endif` closes
9553        // this family. An error-owned terminator inside either branch belongs
9554        // to a damaged nested conditional, not to this one.
9555        return None;
9556    }
9557    let mut displaced = None;
9558    let mut stack = (0..conditional.child_count())
9559        .filter_map(|index| conditional.child(index))
9560        .map(|child| (child, false))
9561        .collect::<Vec<_>>();
9562    while let Some((node, inside_error)) = stack.pop() {
9563        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
9564            continue;
9565        }
9566        if node.kind() == "#endif" && !node.is_missing() && inside_error {
9567            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
9568                displaced = Some(node);
9569            }
9570            continue;
9571        }
9572        if node != conditional
9573            && matches!(
9574                node.kind(),
9575                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
9576            )
9577        {
9578            continue;
9579        }
9580        let inside_error = inside_error || node.kind() == "ERROR";
9581        for index in 0..node.child_count() {
9582            if let Some(child) = node.child(index) {
9583                stack.push((child, inside_error));
9584            }
9585        }
9586    }
9587    displaced
9588}
9589
9590/// The effective end of a conditional whose real terminator tree-sitter
9591/// displaced into declaration recovery.
9592///
9593/// Most damaged conditionals retain a concrete `#endif` token below an
9594/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
9595/// boundary. A preprocessor family that selects the middle of a declaration
9596/// can lose the directive tokens entirely. In that shape tree-sitter leaves
9597/// the declaration's `typedef` token as the sole child of the immediately
9598/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
9599/// declarator name inside the conditional's first declaration. The declaration
9600/// end is then the smallest structured boundary that contains the whole split
9601/// declaration.
9602#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9603pub struct CppDisplacedPreprocessorBoundary {
9604    pub end_byte: usize,
9605    pub end_line: usize,
9606}
9607
9608pub fn cpp_displaced_preprocessor_boundary(
9609    conditional: Node<'_>,
9610) -> Option<CppDisplacedPreprocessorBoundary> {
9611    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
9612        return Some(CppDisplacedPreprocessorBoundary {
9613            end_byte: terminator.end_byte(),
9614            end_line: terminator.end_position().row + 1,
9615        });
9616    }
9617    if let Some(declaration) = displaced_split_declaration(conditional) {
9618        return Some(CppDisplacedPreprocessorBoundary {
9619            end_byte: declaration.end_byte(),
9620            end_line: declaration.end_position().row + 1,
9621        });
9622    }
9623    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
9624        return Some(CppDisplacedPreprocessorBoundary {
9625            end_byte: terminator.end_byte(),
9626            end_line: terminator.end_position().row + 1,
9627        });
9628    }
9629    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
9630        return Some(CppDisplacedPreprocessorBoundary {
9631            end_byte: terminator.end_byte(),
9632            end_line: terminator.end_position().row + 1,
9633        });
9634    }
9635    None
9636}
9637
9638/// Recover an outer terminator that tree-sitter assigned to a damaged nested
9639/// conditional. This occurs when a split construct such as `extern "C"`
9640/// consumes the nested `#endif` inside an error node: the nested conditional's
9641/// direct terminator is then the outer conditional's real terminator, while
9642/// the outer node ends with a missing token and absorbs later declarations.
9643fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
9644    if !conditional.has_error()
9645        || conditional.child_by_field_name("alternative").is_some()
9646        || conditional
9647            .child(conditional.child_count().saturating_sub(1))
9648            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
9649    {
9650        return None;
9651    }
9652    let mut recovered = None;
9653    for index in 0..conditional.named_child_count() {
9654        let Some(nested) = conditional.named_child(index) else {
9655            continue;
9656        };
9657        if !matches!(
9658            nested.kind(),
9659            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
9660        ) || nested.child_by_field_name("alternative").is_some()
9661        {
9662            continue;
9663        }
9664        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
9665            continue;
9666        };
9667        if direct.kind() != "#endif" || direct.is_missing() {
9668            continue;
9669        }
9670        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
9671            continue;
9672        };
9673        if displaced.end_byte() >= direct.start_byte() {
9674            continue;
9675        }
9676        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
9677            recovered = Some(direct);
9678        }
9679    }
9680    recovered
9681}
9682
9683fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
9684    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
9685        return None;
9686    }
9687    let mut cursor = conditional.walk();
9688    let declarations = conditional
9689        .named_children(&mut cursor)
9690        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
9691        .collect::<Vec<_>>();
9692    let declaration = *declarations.first()?;
9693    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
9694        return None;
9695    }
9696    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
9697    let mut terminator = None;
9698    let mut stack = (0..declaration.child_count())
9699        .filter_map(|index| declaration.child(index))
9700        .filter(|child| child.start_byte() < declarator_start)
9701        .map(|child| (child, false))
9702        .collect::<Vec<_>>();
9703    while let Some((node, inside_error)) = stack.pop() {
9704        let inside_error = inside_error || node.kind() == "ERROR";
9705        if inside_error && node.kind() == "#endif" && !node.is_missing() {
9706            terminator = Some(node);
9707            continue;
9708        }
9709        for index in 0..node.child_count() {
9710            if let Some(child) = node.child(index)
9711                && child.start_byte() < declarator_start
9712            {
9713                stack.push((child, inside_error));
9714            }
9715        }
9716    }
9717    terminator
9718}
9719
9720fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
9721    if !conditional.has_error()
9722        || conditional.child_by_field_name("alternative").is_some()
9723        || conditional
9724            .prev_named_sibling()
9725            .filter(|sibling| {
9726                sibling.kind() == "ERROR"
9727                    && sibling.child_count() == 1
9728                    && sibling
9729                        .child(0)
9730                        .is_some_and(|child| child.kind() == "typedef")
9731            })
9732            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
9733            .is_none()
9734    {
9735        return None;
9736    }
9737    let mut cursor = conditional.walk();
9738    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
9739    let declaration_index = children
9740        .iter()
9741        .position(|child| child.kind() == "declaration" && child.has_error())?;
9742    let declaration = children[declaration_index];
9743    if !children
9744        .iter()
9745        .skip(declaration_index + 1)
9746        .any(|child| child.end_byte() > declaration.end_byte())
9747    {
9748        return None;
9749    }
9750    let declarator = declaration.child_by_field_name("declarator")?;
9751    let mut error_end = None;
9752    let mut names = Vec::new();
9753    let mut stack = vec![declarator];
9754    while let Some(node) = stack.pop() {
9755        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
9756            error_end =
9757                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
9758            continue;
9759        }
9760        if matches!(node.kind(), "identifier" | "type_identifier") {
9761            names.push(node.start_byte());
9762        }
9763        for index in (0..node.named_child_count()).rev() {
9764            if let Some(child) = node.named_child(index) {
9765                stack.push(child);
9766            }
9767        }
9768    }
9769    let error_end = error_end?;
9770    names
9771        .into_iter()
9772        .any(|start| start >= error_end)
9773        .then_some(declaration)
9774}
9775
9776fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
9777    let Some(error) = parent.child(error_index) else {
9778        return false;
9779    };
9780    if error.kind() != "ERROR"
9781        || error.child_count() != 1
9782        || error.child(0).is_none_or(|child| child.kind() != "}")
9783    {
9784        return false;
9785    }
9786    let Some(semicolon) = parent.child(error_index + 1) else {
9787        return false;
9788    };
9789    semicolon.kind() == "expression_statement"
9790        && semicolon.child_count() == 1
9791        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
9792}
9793
9794/// Locate the real end of a class-like declaration when a macro invocation
9795/// without a source semicolon absorbs the class's `};` into its parsed field.
9796/// The grammar then keeps following namespace declarations as later children
9797/// of the same field list. The direct ERROR-plus-semicolon pair proves the
9798/// boundary structurally; no source-text delimiter scan is needed.
9799fn displaced_macro_class_tail(
9800    declaration_node: Node<'_>,
9801    body: Node<'_>,
9802    source: &str,
9803) -> Option<DisplacedMacroClassTail> {
9804    if !matches!(
9805        declaration_node.kind(),
9806        "class_specifier" | "struct_specifier" | "union_specifier"
9807    ) || body.kind() != "field_declaration_list"
9808    {
9809        return None;
9810    }
9811
9812    let child_count = body.named_child_count();
9813    for index in 0..child_count {
9814        let child = body.named_child(index)?;
9815        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
9816            continue;
9817        };
9818        let split_index = index + 1;
9819        if split_index >= child_count {
9820            return None;
9821        }
9822        let mut cursor = body.walk();
9823        if !body
9824            .named_children(&mut cursor)
9825            .skip(split_index)
9826            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
9827        {
9828            return None;
9829        }
9830        return Some(DisplacedMacroClassTail {
9831            split_index,
9832            class_range: Range {
9833                start_byte: declaration_node.start_byte(),
9834                end_byte: terminator.end_byte(),
9835                start_line: declaration_node.start_position().row + 1,
9836                end_line: terminator.end_position().row + 1,
9837            },
9838        });
9839    }
9840    None
9841}
9842
9843fn displaced_macro_field_terminator<'tree>(
9844    field: Node<'tree>,
9845    source: &str,
9846) -> Option<Node<'tree>> {
9847    if field.kind() != "field_declaration" {
9848        return None;
9849    }
9850    let macro_type = field.child_by_field_name("type")?;
9851    if macro_type.kind() != "type_identifier"
9852        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9853        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
9854    {
9855        return None;
9856    }
9857    for index in 0..field.child_count() {
9858        let error = field.child(index)?;
9859        if error.kind() != "ERROR"
9860            || error.child_count() != 1
9861            || error.child(0).is_none_or(|child| child.kind() != "}")
9862        {
9863            continue;
9864        }
9865        let semicolon = field.child(index + 1)?;
9866        if semicolon.kind() == ";" {
9867            return Some(semicolon);
9868        }
9869    }
9870    None
9871}
9872
9873fn recover_fragmented_partial_specialization<'tree>(
9874    template_node: Node<'tree>,
9875    declaration_child: Node<'tree>,
9876    source: &str,
9877    ancestry: &ParentIndex<'tree>,
9878) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
9879    if declaration_child.kind() != "function_definition" {
9880        return None;
9881    }
9882    let class_node = declaration_child.child_by_field_name("type")?;
9883    if !matches!(
9884        class_node.kind(),
9885        "class_specifier" | "struct_specifier" | "union_specifier"
9886    ) || !class_node
9887        .child_by_field_name("name")
9888        .and_then(|name| direct_identifier_name(name, source))
9889        .is_some_and(|name| cpp_export_macro_token(&name))
9890    {
9891        return None;
9892    }
9893    let declarator = declaration_child.child_by_field_name("declarator")?;
9894    if declarator.kind() != "template_function" {
9895        return None;
9896    }
9897    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
9898    if metadata.specialization_arguments.is_empty() {
9899        return None;
9900    }
9901    let body = declaration_child.child_by_field_name("body")?;
9902    if body.kind() != "compound_statement" {
9903        return None;
9904    }
9905    let complete_prefix = body.named_child(0).filter(|first| {
9906        first.kind() == "labeled_statement"
9907            && first.has_error()
9908            && first
9909                .named_child(first.named_child_count().saturating_sub(1))
9910                .is_some_and(recovered_declaration_has_class_terminator)
9911    });
9912    let complete_body = complete_prefix.is_some();
9913    let mut prefix_members = Vec::new();
9914    if let Some(prefix) = complete_prefix {
9915        prefix_members.push(prefix);
9916    } else {
9917        let mut body_cursor = body.walk();
9918        for child in body.named_children(&mut body_cursor) {
9919            if !is_structurally_valid_fragmented_class_prefix_member(child) {
9920                break;
9921            }
9922            prefix_members.push(child);
9923        }
9924    }
9925    let containing_declarations = template_node.parent()?;
9926    if !matches!(
9927        containing_declarations.kind(),
9928        "declaration_list" | "compound_statement"
9929    ) {
9930        return None;
9931    }
9932    let mut member_siblings = Vec::new();
9933    let mut following_declarations = Vec::new();
9934    let terminator;
9935    if complete_body {
9936        terminator = complete_prefix?;
9937        let mut cursor = body.walk();
9938        let mut after_prefix = false;
9939        for child in body.named_children(&mut cursor) {
9940            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
9941                after_prefix = true;
9942            } else if after_prefix {
9943                following_declarations.push(child);
9944            }
9945        }
9946    } else {
9947        let mut found_template = false;
9948        let mut cursor = containing_declarations.walk();
9949        let mut class_terminator = None;
9950        for child in containing_declarations.children(&mut cursor) {
9951            if same_node(child, template_node) {
9952                found_template = true;
9953                continue;
9954            }
9955            if found_template && child.kind() == "}" {
9956                class_terminator = Some(child);
9957                break;
9958            }
9959            // A namespace can never be a class member: reaching one before the
9960            // terminator proves the class's true close was swallowed upstream
9961            // and this scan has crossed into the enclosing scope, so the
9962            // recovery cannot be bounded -- continuing re-owns the namespace
9963            // block (and its template specializations) as class members under
9964            // a re-appended package, desyncing the fq boundary (#2306).
9965            if found_template && child.kind() == "namespace_definition" {
9966                return None;
9967            }
9968            if found_template && child.is_named() {
9969                member_siblings.push(child);
9970            }
9971        }
9972        terminator = class_terminator?;
9973    }
9974    let name = format!(
9975        "{}<{}>",
9976        metadata.primary_name,
9977        metadata
9978            .specialization_arguments
9979            .iter()
9980            .map(|argument| argument.text.as_str())
9981            .collect::<Vec<_>>()
9982            .join(", ")
9983    );
9984    Some(RecoveredFragmentedPartialSpecialization {
9985        declaration_node: declaration_child,
9986        name,
9987        range: Range {
9988            start_byte: declaration_child.start_byte(),
9989            end_byte: terminator.end_byte(),
9990            start_line: declaration_child.start_position().row + 1,
9991            end_line: terminator.end_position().row + 1,
9992        },
9993        prefix_members,
9994        member_siblings,
9995        following_declarations,
9996    })
9997}
9998
9999fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
10000    if declaration.kind() != "declaration" {
10001        return false;
10002    }
10003    // With an export macro between `class` and its name, tree-sitter folds a
10004    // complete class body into a function-shaped declaration. The class's own
10005    // `};` remains structurally identifiable as a direct ERROR child holding
10006    // `}`, immediately followed by the declaration's direct `;` child.
10007    (0..declaration.child_count().saturating_sub(1)).any(|index| {
10008        let Some(error) = declaration.child(index) else {
10009            return false;
10010        };
10011        error.kind() == "ERROR"
10012            && error.child_count() == 1
10013            && error.child(0).is_some_and(|child| child.kind() == "}")
10014            && declaration
10015                .child(index + 1)
10016                .is_some_and(|child| child.kind() == ";")
10017    })
10018}
10019
10020fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
10021    if node.has_error() {
10022        return false;
10023    }
10024    match node.kind() {
10025        "declaration"
10026        | "field_declaration"
10027        | "alias_declaration"
10028        | "type_definition"
10029        | "static_assert_declaration" => true,
10030        "labeled_statement" => node
10031            .named_child(node.named_child_count().saturating_sub(1))
10032            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
10033        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
10034            matches!(
10035                child.kind(),
10036                "declaration"
10037                    | "field_declaration"
10038                    | "alias_declaration"
10039                    | "type_definition"
10040                    | "function_definition"
10041            )
10042        }),
10043        _ => false,
10044    }
10045}
10046
10047fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
10048    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
10049        .then(|| node.child_by_field_name("declarator"))
10050        .flatten()
10051        .and_then(|declarator| extract_variable_name(declarator, source))
10052}
10053
10054fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
10055    while let Some(parent) = node.parent() {
10056        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
10057            return true;
10058        }
10059        node = parent;
10060    }
10061    false
10062}
10063
10064fn cpp_template_metadata<'tree>(
10065    template_node: Node<'tree>,
10066    declaration_child: Node<'tree>,
10067    source: &str,
10068    ancestry: &ParentIndex<'tree>,
10069) -> Option<CppTemplateMetadata> {
10070    let parameters_node = template_node.child_by_field_name("parameters")?;
10071    let name_node = cpp_templated_class_name_node(declaration_child)?;
10072    let primary_node = match name_node.kind() {
10073        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
10074        _ => name_node,
10075    };
10076    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
10077    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
10078        return None;
10079    }
10080
10081    let mut parameter_nodes = Vec::new();
10082    let mut parameter_names = Vec::new();
10083    let mut cursor = parameters_node.walk();
10084    for parameter in parameters_node.named_children(&mut cursor) {
10085        if !matches!(
10086            parameter.kind(),
10087            "type_parameter_declaration"
10088                | "optional_type_parameter_declaration"
10089                | "variadic_type_parameter_declaration"
10090                | "template_template_parameter_declaration"
10091                | "parameter_declaration"
10092                | "optional_parameter_declaration"
10093                | "variadic_parameter_declaration"
10094        ) {
10095            continue;
10096        }
10097        let index = parameter_nodes.len();
10098        // An unnamed parameter still contributes template arity and kind. Use
10099        // an impossible C++ identifier so positional reconciliation can bind
10100        // it without making source expressions refer to a name that was not
10101        // written.
10102        let name = cpp_template_parameter_name(parameter, source)
10103            .unwrap_or_else(|| format!("<anonymous:{index}>"));
10104        parameter_names.push(name);
10105        parameter_nodes.push(parameter);
10106    }
10107    let parameters = parameter_nodes
10108        .into_iter()
10109        .zip(parameter_names.iter().cloned())
10110        .map(|(parameter, name)| CppTemplateParameterMetadata {
10111            name,
10112            kind: cpp_template_parameter_kind(parameter),
10113            variadic: matches!(
10114                parameter.kind(),
10115                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
10116            ),
10117            default: cpp_template_parameter_default_expression(
10118                parameter,
10119                source,
10120                &parameter_names,
10121                ancestry,
10122            ),
10123        })
10124        .collect();
10125    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
10126        Vec::new()
10127    } else {
10128        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
10129            .unwrap_or_default()
10130    };
10131    let alias_target = (declaration_child.kind() == "alias_declaration")
10132        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
10133        .flatten();
10134    Some(CppTemplateMetadata {
10135        primary_name,
10136        primary_fq_name: String::new(),
10137        parameters,
10138        specialization_arguments,
10139        alias_target,
10140    })
10141}
10142
10143fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
10144    match node.kind() {
10145        "class_specifier" | "struct_specifier" | "union_specifier" => {
10146            node.child_by_field_name("name")
10147        }
10148        "function_definition" => {
10149            let declarator = node.child_by_field_name("declarator")?;
10150            if matches!(declarator.kind(), "identifier" | "template_function") {
10151                Some(declarator)
10152            } else {
10153                None
10154            }
10155        }
10156        "alias_declaration" => node.child_by_field_name("name"),
10157        _ => None,
10158    }
10159}
10160
10161fn cpp_template_alias_target<'tree>(
10162    alias: Node<'tree>,
10163    source: &str,
10164    parameter_names: &[String],
10165    ancestry: &ParentIndex<'tree>,
10166) -> Option<CppTemplateAliasTargetMetadata> {
10167    let mut type_node = alias.child_by_field_name("type")?;
10168    while type_node.kind() == "type_descriptor" {
10169        type_node = type_node.child_by_field_name("type")?;
10170    }
10171    let global = type_node.child_by_field_name("scope").is_none()
10172        && type_node.child(0).is_some_and(|child| child.kind() == "::");
10173    let mut components = Vec::new();
10174    cpp_template_target_components(type_node, source, &mut components)?;
10175    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
10176    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
10177        components,
10178        global,
10179        arguments,
10180    })
10181}
10182
10183fn cpp_template_target_components(
10184    node: Node<'_>,
10185    source: &str,
10186    out: &mut Vec<String>,
10187) -> Option<()> {
10188    match node.kind() {
10189        "identifier" | "namespace_identifier" | "type_identifier" => {
10190            out.push(node_text(node, source).to_string());
10191            Some(())
10192        }
10193        "template_type" => {
10194            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
10195        }
10196        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10197            if let Some(scope) = node.child_by_field_name("scope") {
10198                cpp_template_target_components(scope, source, out)?;
10199            }
10200            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
10201        }
10202        _ => None,
10203    }
10204}
10205
10206fn cpp_template_argument_expressions<'tree>(
10207    mut node: Node<'tree>,
10208    source: &str,
10209    parameter_names: &[String],
10210    ancestry: &ParentIndex<'tree>,
10211) -> Option<Vec<CppTemplateExpression>> {
10212    loop {
10213        match node.kind() {
10214            "template_type" | "template_function" => {
10215                let arguments = node.child_by_field_name("arguments")?;
10216                let mut cursor = arguments.walk();
10217                return Some(
10218                    arguments
10219                        .named_children(&mut cursor)
10220                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
10221                        .map(|argument| {
10222                            cpp_template_expression(argument, source, parameter_names, ancestry)
10223                        })
10224                        .collect(),
10225                );
10226            }
10227            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
10228                node = node
10229                    .child_by_field_name("name")
10230                    .or_else(|| node.child_by_field_name("type"))?;
10231            }
10232            _ => return None,
10233        }
10234    }
10235}
10236
10237fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
10238    let candidate = node
10239        .child_by_field_name("name")
10240        .or_else(|| node.child_by_field_name("declarator"))
10241        .or_else(|| {
10242            let mut cursor = node.walk();
10243            node.named_children(&mut cursor).find(|child| {
10244                matches!(
10245                    child.kind(),
10246                    "identifier" | "type_identifier" | "field_identifier"
10247                )
10248            })
10249        })?;
10250    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
10251    (!name.is_empty()).then_some(name)
10252}
10253
10254fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
10255    match node.kind() {
10256        "type_parameter_declaration"
10257        | "optional_type_parameter_declaration"
10258        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
10259        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
10260        _ => CppTemplateParameterKind::Value,
10261    }
10262}
10263
10264fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
10265    node.child_by_field_name("default_type")
10266        .or_else(|| node.child_by_field_name("default_value"))
10267}
10268
10269fn cpp_template_parameter_default_expression<'tree>(
10270    parameter: Node<'tree>,
10271    source: &str,
10272    parameter_names: &[String],
10273    ancestry: &ParentIndex<'tree>,
10274) -> Option<CppTemplateExpression> {
10275    let default = cpp_template_parameter_default(parameter)?;
10276    let base = cpp_template_expression(default, source, parameter_names, ancestry);
10277    let Some(pointer_error) = parameter.next_named_sibling() else {
10278        return Some(base);
10279    };
10280    let Some(pointer_declarator) =
10281        recovered_abstract_pointer_declarator_term(pointer_error, source)
10282    else {
10283        return Some(base);
10284    };
10285    Some(CppTemplateExpression {
10286        text: format!(
10287            "{}{}",
10288            base.text,
10289            normalize_cpp_whitespace(node_text(pointer_error, source))
10290        ),
10291        term: CppTemplateTerm::Node {
10292            kind: "type_descriptor".to_string(),
10293            children: vec![base.term, pointer_declarator],
10294        },
10295    })
10296}
10297
10298fn recovered_abstract_pointer_declarator_term(
10299    node: Node<'_>,
10300    source: &str,
10301) -> Option<CppTemplateTerm> {
10302    if node.kind() != "ERROR" || node.child_count() == 0 {
10303        return None;
10304    }
10305    let mut children = Vec::new();
10306    for index in 0..node.child_count() {
10307        let child = node.child(index)?;
10308        if child.kind() != "*" {
10309            return None;
10310        }
10311        children.push(CppTemplateTerm::Atom {
10312            kind: "*".to_string(),
10313            text: normalize_cpp_whitespace(node_text(child, source)),
10314        });
10315    }
10316    Some(CppTemplateTerm::Node {
10317        kind: "abstract_pointer_declarator".to_string(),
10318        children,
10319    })
10320}
10321
10322fn cpp_template_expression<'tree>(
10323    node: Node<'tree>,
10324    source: &str,
10325    parameter_names: &[String],
10326    ancestry: &ParentIndex<'tree>,
10327) -> CppTemplateExpression {
10328    let text = normalize_cpp_whitespace(node_text(node, source));
10329    CppTemplateExpression {
10330        text,
10331        term: cpp_template_term(node, source, parameter_names, ancestry),
10332    }
10333}
10334
10335pub fn cpp_template_term<'tree>(
10336    node: Node<'tree>,
10337    source: &str,
10338    parameter_names: &[String],
10339    ancestry: &ParentIndex<'tree>,
10340) -> CppTemplateTerm {
10341    enum Work<'tree> {
10342        Visit(Node<'tree>),
10343        Build { kind: String, child_count: usize },
10344    }
10345
10346    let mut work = vec![Work::Visit(node)];
10347    let mut terms = Vec::new();
10348    while let Some(next) = work.pop() {
10349        match next {
10350            Work::Visit(current) => {
10351                let text = normalize_cpp_whitespace(node_text(current, source));
10352                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
10353                    terms.push(CppTemplateTerm::Parameter(text));
10354                    continue;
10355                }
10356                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
10357                    let mut cursor = current.walk();
10358                    let named = current
10359                        .named_children(&mut cursor)
10360                        .filter(|child| !child.is_extra() && child.kind() != "comment")
10361                        .collect::<Vec<_>>();
10362                    if let [child] = named.as_slice() {
10363                        work.push(Work::Visit(*child));
10364                        continue;
10365                    }
10366                }
10367                if current.child_count() == 0 {
10368                    terms.push(CppTemplateTerm::Atom {
10369                        kind: if matches!(
10370                            current.kind(),
10371                            "identifier"
10372                                | "type_identifier"
10373                                | "field_identifier"
10374                                | "namespace_identifier"
10375                        ) {
10376                            "identifier".to_string()
10377                        } else {
10378                            current.kind().to_string()
10379                        },
10380                        text,
10381                    });
10382                    continue;
10383                }
10384                let children = (0..current.child_count())
10385                    .filter_map(|index| current.child(index))
10386                    .filter(|child| !child.is_extra() && child.kind() != "comment")
10387                    .collect::<Vec<_>>();
10388                work.push(Work::Build {
10389                    kind: current.kind().to_string(),
10390                    child_count: children.len(),
10391                });
10392                work.extend(children.into_iter().rev().map(Work::Visit));
10393            }
10394            Work::Build { kind, child_count } => {
10395                let children = terms.split_off(terms.len() - child_count);
10396                terms.push(CppTemplateTerm::Node { kind, children });
10397            }
10398        }
10399    }
10400    terms.pop().expect("template term traversal emits one root")
10401}
10402
10403fn cpp_template_term_leaf_is_parameter<'tree>(
10404    node: Node<'tree>,
10405    text: &str,
10406    parameter_names: &[String],
10407    ancestry: &ParentIndex<'tree>,
10408) -> bool {
10409    if !parameter_names.iter().any(|parameter| parameter == text) {
10410        return false;
10411    }
10412    !ancestry.parent(node).is_some_and(|parent| {
10413        matches!(
10414            parent.kind(),
10415            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
10416        ) && parent.child_by_field_name("scope").is_some()
10417            && parent.child_by_field_name("name") == Some(node)
10418    })
10419}
10420
10421fn enclosing_cpp_declaration_node<'tree>(
10422    mut node: Node<'tree>,
10423    ancestry: &ParentIndex<'tree>,
10424) -> Option<Node<'tree>> {
10425    loop {
10426        match node.kind() {
10427            "declaration"
10428            | "function_declaration"
10429            | "field_declaration"
10430            | "function_definition" => return Some(node),
10431            _ => node = ancestry.parent(node)?,
10432        }
10433    }
10434}
10435
10436fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
10437    let mut params = Vec::new();
10438    let mut cursor = parameters_node.walk();
10439    for child in parameters_node.children(&mut cursor) {
10440        match child.kind() {
10441            "parameter_declaration" | "optional_parameter_declaration" => {
10442                params.push(cpp_parameter_type(child, source));
10443            }
10444            "variadic_parameter_declaration" => {
10445                params.push(cpp_parameter_type(child, source));
10446            }
10447            "variadic_parameter" | "..." => params.push("...".to_string()),
10448            _ => {}
10449        }
10450    }
10451
10452    if params.is_empty() {
10453        "()".to_string()
10454    } else {
10455        format!("({})", params.join(", "))
10456    }
10457}
10458
10459fn cpp_signature_metadata<'tree>(
10460    signature: String,
10461    function_declarator: Node<'tree>,
10462    source: &str,
10463    ancestry: &ParentIndex<'tree>,
10464) -> SignatureMetadata {
10465    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
10466    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
10467    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
10468    let return_type_identity =
10469        cpp_callable_return_type_identity(function_declarator, source, ancestry);
10470    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
10471        return enrich(
10472            SignatureMetadata::new(signature, Vec::new())
10473                .with_return_type_text(return_type_text)
10474                .with_return_type_identity(return_type_identity),
10475        );
10476    };
10477    let callable_arity = cpp_callable_arity(parameters_node, source);
10478    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
10479    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
10480    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
10481    let Some(relative_start) = signature
10482        .get(search_from..)
10483        .and_then(|suffix| suffix.find(&parameter_text))
10484    else {
10485        return enrich(
10486            SignatureMetadata::new(signature, Vec::new())
10487                .with_callable_arity(callable_arity)
10488                .with_callable_parameter_types(callable_parameter_types)
10489                .with_return_type_text(return_type_text)
10490                .with_return_type_identity(return_type_identity),
10491        );
10492    };
10493    let parameters_start = search_from + relative_start;
10494    let parameters_end = parameters_start + parameter_text.len();
10495    let mut search_start = parameters_start;
10496    let parameters = cpp_parameter_label_nodes(parameters_node)
10497        .into_iter()
10498        .filter_map(|label_node| {
10499            let label = normalize_cpp_whitespace(node_text(label_node, source));
10500            if label.is_empty() || search_start > parameters_end {
10501                return None;
10502            }
10503            let haystack = signature.get(search_start..parameters_end)?;
10504            let relative_start = haystack.find(&label)?;
10505            let start_byte = search_start + relative_start;
10506            let end_byte = start_byte + label.len();
10507            search_start = end_byte;
10508            Some(ParameterMetadata::new(label, start_byte, end_byte))
10509        })
10510        .collect();
10511    enrich(
10512        SignatureMetadata::new(signature, parameters)
10513            .with_callable_arity(callable_arity)
10514            .with_callable_parameter_types(callable_parameter_types)
10515            .with_return_type_text(return_type_text)
10516            .with_return_type_identity(return_type_identity),
10517    )
10518}
10519
10520fn cpp_callable_is_structural_constructor<'tree>(
10521    function_declarator: Node<'tree>,
10522    source: &str,
10523    ancestry: &ParentIndex<'tree>,
10524) -> bool {
10525    let Some(name_node) = function_declarator
10526        .child_by_field_name("declarator")
10527        .or_else(|| function_declarator.child_by_field_name("name"))
10528        .or_else(|| last_named_child(function_declarator))
10529    else {
10530        return false;
10531    };
10532    let Some(callable_name) = direct_identifier_name(name_node, source) else {
10533        return false;
10534    };
10535
10536    let mut current = ancestry.parent(function_declarator);
10537    while let Some(ancestor) = current {
10538        let owner_name = match ancestor.kind() {
10539            "class_specifier" | "struct_specifier" | "union_specifier" => {
10540                class_like_name(ancestor, source, ancestry)
10541            }
10542            "ERROR" => malformed_class_error_owner_name(ancestor, source),
10543            _ => None,
10544        };
10545        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
10546            return true;
10547        }
10548        current = ancestry.parent(ancestor);
10549    }
10550    false
10551}
10552
10553/// Recover the owner name from the direct grammar shape retained when a later
10554/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
10555/// `ERROR` node:
10556///
10557/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
10558///
10559/// Direct-child checks keep this distinct from an unrelated nested class inside
10560/// a broader error region. The closing brace may be displaced past the error
10561/// node, so the opening body token is the available structural boundary.
10562fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
10563    if node.kind() != "ERROR" {
10564        return None;
10565    }
10566    let keyword = node.child(0)?;
10567    if !matches!(keyword.kind(), "class" | "struct" | "union") {
10568        return None;
10569    }
10570    let name_node = node.child(1)?;
10571    let name = direct_identifier_name(name_node, source)?;
10572    let has_body = (2..node.child_count())
10573        .filter_map(|index| node.child(index))
10574        .any(|child| child.kind() == "{");
10575    has_body.then_some(name)
10576}
10577
10578/// The parser-derived return type of one callable declaration.
10579///
10580/// This accepts the enclosing declaration node so consumers do not need to
10581/// duplicate the declarator-unwrapping rules before asking for the structured
10582/// identity.
10583pub fn cpp_callable_declaration_return_type_identity<'tree>(
10584    callable: Node<'tree>,
10585    source: &str,
10586    ancestry: &ParentIndex<'tree>,
10587) -> Option<StructuredTypeIdentity> {
10588    let declarator = callable
10589        .child_by_field_name("declarator")
10590        .and_then(extract_function_declarator)?;
10591    cpp_callable_return_type_identity(declarator, source, ancestry)
10592}
10593
10594pub(crate) fn cpp_callable_return_type_identity<'tree>(
10595    function_declarator: Node<'tree>,
10596    source: &str,
10597    ancestry: &ParentIndex<'tree>,
10598) -> Option<StructuredTypeIdentity> {
10599    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
10600        return None;
10601    }
10602    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
10603    if let Some((return_type, _)) =
10604        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
10605    {
10606        return cpp_structured_type_identity(return_type, source, &lexical_scope);
10607    }
10608    let mut cursor = function_declarator.walk();
10609    if let Some(trailing) = function_declarator
10610        .named_children(&mut cursor)
10611        .find(|child| child.kind() == "trailing_return_type")
10612        && let Some(type_descriptor) = trailing.named_child(0)
10613    {
10614        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
10615    }
10616
10617    let mut current = function_declarator;
10618    let mut wrappers = Vec::new();
10619    while let Some(parent) = ancestry.parent(current) {
10620        if matches!(
10621            parent.kind(),
10622            "function_definition" | "declaration" | "field_declaration"
10623        ) {
10624            let type_node = parent.child_by_field_name("type")?;
10625            if cpp_export_macro_token(node_text(type_node, source))
10626                && (0..parent.named_child_count()).any(|index| {
10627                    parent
10628                        .named_child(index)
10629                        .is_some_and(|child| child.kind() == "ERROR")
10630                })
10631            {
10632                return None;
10633            }
10634            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
10635            for wrapper in wrappers.into_iter().rev() {
10636                identity = cpp_wrap_structured_type(identity, wrapper)?;
10637            }
10638            return Some(identity);
10639        }
10640        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
10641            || (matches!(
10642                parent.kind(),
10643                "pointer_declarator"
10644                    | "reference_declarator"
10645                    | "array_declarator"
10646                    | "parenthesized_declarator"
10647            ) && parent.named_child_count() == 1
10648                && parent.named_child(0) == Some(current));
10649        if !wraps_current_declarator {
10650            return None;
10651        }
10652        match parent.kind() {
10653            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
10654            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
10655            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
10656            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
10657            _ => return None,
10658        }
10659        current = parent;
10660    }
10661    None
10662}
10663
10664fn cpp_structured_type_identity(
10665    node: Node<'_>,
10666    source: &str,
10667    lexical_scope: &[String],
10668) -> Option<StructuredTypeIdentity> {
10669    enum Work<'tree> {
10670        Visit(Node<'tree>),
10671        Wrap(CppStructuredTypeWrapper),
10672        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
10673        BuildGeneric { argument_count: usize },
10674    }
10675
10676    let mut work = vec![Work::Visit(node)];
10677    let mut values = Vec::new();
10678    let mut builder = StructuredTypeIdentityBuilder::default();
10679    while let Some(next) = work.pop() {
10680        match next {
10681            Work::Visit(current) => match current.kind() {
10682                "type_descriptor" => {
10683                    let type_node = current
10684                        .child_by_field_name("type")
10685                        .or_else(|| current.named_child(0))?;
10686                    let mut wrappers = Vec::new();
10687                    let mut cursor = current.walk();
10688                    for child in current.named_children(&mut cursor) {
10689                        if child.id() != type_node.id() {
10690                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
10691                        }
10692                    }
10693                    work.push(Work::ApplyWrappers(wrappers));
10694                    work.push(Work::Visit(type_node));
10695                }
10696                "pointer_declarator" | "abstract_pointer_declarator" => {
10697                    let child = current
10698                        .child_by_field_name("declarator")
10699                        .or_else(|| current.named_child(0))?;
10700                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
10701                    work.push(Work::Visit(child));
10702                }
10703                "reference_declarator" => {
10704                    let child = current
10705                        .child_by_field_name("declarator")
10706                        .or_else(|| current.named_child(0))?;
10707                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
10708                    work.push(Work::Visit(child));
10709                }
10710                "array_declarator" | "abstract_array_declarator" => {
10711                    let child = current
10712                        .child_by_field_name("declarator")
10713                        .or_else(|| current.named_child(0))?;
10714                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
10715                    work.push(Work::Visit(child));
10716                }
10717                "template_type" => {
10718                    let name_node = current.child_by_field_name("name")?;
10719                    let arguments = current
10720                        .child_by_field_name("arguments")
10721                        .map(|arguments_node| {
10722                            let mut cursor = arguments_node.walk();
10723                            arguments_node
10724                                .named_children(&mut cursor)
10725                                .filter(|child| !child.is_extra() && child.kind() != "comment")
10726                                .collect::<Vec<_>>()
10727                        })
10728                        .unwrap_or_default();
10729                    work.push(Work::BuildGeneric {
10730                        argument_count: arguments.len(),
10731                    });
10732                    work.extend(arguments.into_iter().rev().map(Work::Visit));
10733                    work.push(Work::Visit(name_node));
10734                }
10735                "qualified_identifier"
10736                | "scoped_identifier"
10737                | "scoped_type_identifier"
10738                | "type_identifier"
10739                | "field_identifier"
10740                | "identifier"
10741                | "namespace_identifier"
10742                | "primitive_type" => {
10743                    values.push(builder.named(cpp_structured_named_type(
10744                        current,
10745                        source,
10746                        lexical_scope,
10747                    )?)?);
10748                }
10749                _ => {
10750                    let child = current.child_by_field_name("type").or_else(|| {
10751                        (current.named_child_count() == 1)
10752                            .then(|| current.named_child(0))
10753                            .flatten()
10754                    })?;
10755                    work.push(Work::Visit(child));
10756                }
10757            },
10758            Work::Wrap(wrapper) => {
10759                let root = values.pop()?;
10760                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
10761            }
10762            Work::ApplyWrappers(wrappers) => {
10763                let mut root = values.pop()?;
10764                for wrapper in wrappers.into_iter().rev() {
10765                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
10766                }
10767                values.push(root);
10768            }
10769            Work::BuildGeneric { argument_count } => {
10770                let value_count = argument_count.checked_add(1)?;
10771                let start = values.len().checked_sub(value_count)?;
10772                let mut built = values.split_off(start);
10773                let base = built.remove(0);
10774                values.push(builder.generic(base, built)?);
10775            }
10776        }
10777    }
10778    (values.len() == 1)
10779        .then(|| values.pop())
10780        .flatten()
10781        .and_then(|root| builder.finish(root))
10782}
10783
10784fn cpp_structured_named_type(
10785    node: Node<'_>,
10786    source: &str,
10787    lexical_scope: &[String],
10788) -> Option<StructuredTypeName> {
10789    let path = cpp_structured_type_path(node, source)?;
10790    let absolute = node.child_by_field_name("scope").is_none()
10791        && node.child(0).is_some_and(|child| child.kind() == "::");
10792    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
10793}
10794
10795#[derive(Clone, Copy)]
10796enum CppStructuredTypeWrapper {
10797    Pointer,
10798    LvalueReference,
10799    RvalueReference,
10800    Array,
10801}
10802
10803fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
10804    let mut wrappers = Vec::new();
10805    let mut current = node;
10806    loop {
10807        match current.kind() {
10808            "pointer_declarator" | "abstract_pointer_declarator" => {
10809                wrappers.push(CppStructuredTypeWrapper::Pointer)
10810            }
10811            "reference_declarator" | "abstract_reference_declarator" => {
10812                wrappers.push(cpp_reference_wrapper(current)?);
10813            }
10814            "array_declarator" | "abstract_array_declarator" => {
10815                wrappers.push(CppStructuredTypeWrapper::Array)
10816            }
10817            _ => break,
10818        }
10819        let Some(child) = current
10820            .child_by_field_name("declarator")
10821            .or_else(|| current.named_child(0))
10822        else {
10823            break;
10824        };
10825        current = child;
10826    }
10827    Some(wrappers)
10828}
10829
10830fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
10831    node.children(&mut node.walk())
10832        .find_map(|child| match child.kind() {
10833            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
10834            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
10835            _ => None,
10836        })
10837}
10838
10839fn cpp_wrap_structured_type(
10840    identity: StructuredTypeIdentity,
10841    wrapper: CppStructuredTypeWrapper,
10842) -> Option<StructuredTypeIdentity> {
10843    match wrapper {
10844        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
10845        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
10846        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
10847        CppStructuredTypeWrapper::Array => identity.wrap_array(),
10848    }
10849}
10850
10851fn cpp_wrap_structured_type_node(
10852    builder: &mut StructuredTypeIdentityBuilder,
10853    inner: StructuredTypeNodeId,
10854    wrapper: CppStructuredTypeWrapper,
10855) -> Option<StructuredTypeNodeId> {
10856    match wrapper {
10857        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
10858        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
10859        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
10860        CppStructuredTypeWrapper::Array => builder.array(inner),
10861    }
10862}
10863
10864fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
10865    let mut path = Vec::new();
10866    let mut stack = vec![node];
10867    while let Some(current) = stack.pop() {
10868        match current.kind() {
10869            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
10870                let component = node_text(current, source).to_string();
10871                if component.is_empty() {
10872                    return None;
10873                }
10874                path.push(component);
10875            }
10876            "template_type" | "dependent_type" => {
10877                stack.push(current.child_by_field_name("name")?);
10878            }
10879            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10880                stack.push(current.child_by_field_name("name")?);
10881                if let Some(scope) = current.child_by_field_name("scope") {
10882                    stack.push(scope);
10883                }
10884            }
10885            _ => return None,
10886        }
10887    }
10888    (!path.is_empty()).then_some(path)
10889}
10890
10891fn cpp_callable_lexical_scope<'tree>(
10892    node: Node<'tree>,
10893    source: &str,
10894    ancestry: &ParentIndex<'tree>,
10895) -> Vec<String> {
10896    let mut groups = Vec::new();
10897    let mut current = ancestry.parent(node);
10898    while let Some(parent) = current {
10899        if matches!(
10900            parent.kind(),
10901            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
10902        ) && let Some(name_node) = parent.child_by_field_name("name")
10903            && let Some(components) = cpp_structured_type_path(name_node, source)
10904            && !components.is_empty()
10905        {
10906            groups.push(components);
10907        }
10908        current = ancestry.parent(parent);
10909    }
10910    groups.reverse();
10911    groups.into_iter().flatten().collect()
10912}
10913
10914fn cpp_callable_dispatch_extensibility<'tree>(
10915    function_declarator: Node<'tree>,
10916    ancestry: &ParentIndex<'tree>,
10917) -> DispatchExtensibility {
10918    let mut declaration = None;
10919    let mut current = Some(function_declarator);
10920    while let Some(node) = current {
10921        match node.kind() {
10922            "template_declaration"
10923            | "preproc_if"
10924            | "preproc_ifdef"
10925            | "preproc_else"
10926            | "preproc_elif"
10927            | "preproc_call"
10928            | "ERROR" => return DispatchExtensibility::Open,
10929            "declaration" | "field_declaration" | "function_definition" => {
10930                declaration.get_or_insert(node);
10931            }
10932            "translation_unit" => break,
10933            _ => {}
10934        }
10935        current = ancestry.parent(node);
10936    }
10937    let Some(declaration) = declaration else {
10938        return DispatchExtensibility::Open;
10939    };
10940
10941    let mut saw_virtual_boundary = false;
10942    let mut stack = vec![declaration];
10943    while let Some(node) = stack.pop() {
10944        match node.kind() {
10945            "compound_statement" | "field_declaration_list" => continue,
10946            "final" | "final_specifier" => return DispatchExtensibility::Closed,
10947            "virtual"
10948            | "override"
10949            | "virtual_specifier"
10950            | "pure_virtual_clause"
10951            | "template_parameter_list"
10952            | "template_method"
10953            | "template_function"
10954            | "ERROR" => saw_virtual_boundary = true,
10955            _ => {}
10956        }
10957        let mut cursor = node.walk();
10958        stack.extend(node.children(&mut cursor));
10959    }
10960
10961    if saw_virtual_boundary {
10962        DispatchExtensibility::Open
10963    } else {
10964        DispatchExtensibility::Closed
10965    }
10966}
10967
10968fn cpp_callable_linkage<'tree>(
10969    declaration: Node<'tree>,
10970    source: &str,
10971    ancestry: &ParentIndex<'tree>,
10972) -> CallableLinkage {
10973    let mut enclosed_by_class = false;
10974    let mut current = ancestry.parent(declaration);
10975    while let Some(node) = current {
10976        if node.kind() == "namespace_definition"
10977            && node
10978                .child_by_field_name("name")
10979                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10980        {
10981            return CallableLinkage::Internal;
10982        }
10983        if matches!(
10984            node.kind(),
10985            "class_specifier" | "struct_specifier" | "union_specifier"
10986        ) {
10987            if node
10988                .child_by_field_name("name")
10989                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
10990            {
10991                return CallableLinkage::Internal;
10992            }
10993            enclosed_by_class = true;
10994        }
10995        // An export macro between `class` and the class name can make
10996        // tree-sitter parse the entire class as a function definition. The
10997        // structured recovery proves that this container is a named class
10998        // scope, not a local callable scope.
10999        if node.kind() == "lambda_expression"
11000            || node.kind() == "function_definition"
11001                && !is_recovered_exported_class_container(node, source)
11002        {
11003            return CallableLinkage::Internal;
11004        }
11005        current = ancestry.parent(node);
11006    }
11007
11008    if enclosed_by_class {
11009        return CallableLinkage::External;
11010    }
11011
11012    let mut cursor = declaration.walk();
11013    if declaration.named_children(&mut cursor).any(|child| {
11014        child.kind() == "storage_class_specifier"
11015            && normalize_cpp_whitespace(node_text(child, source)) == "static"
11016    }) {
11017        CallableLinkage::Internal
11018    } else {
11019        CallableLinkage::External
11020    }
11021}
11022
11023fn cpp_callable_return_type_text<'tree>(
11024    function_declarator: Node<'tree>,
11025    source: &str,
11026    ancestry: &ParentIndex<'tree>,
11027) -> Option<String> {
11028    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11029        return None;
11030    }
11031    if let Some((return_type, _)) =
11032        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11033    {
11034        let text = normalize_cpp_whitespace(node_text(return_type, source));
11035        return (!text.is_empty()).then_some(text);
11036    }
11037    let mut cursor = function_declarator.walk();
11038    if let Some(trailing) = function_declarator
11039        .named_children(&mut cursor)
11040        .find(|child| child.kind() == "trailing_return_type")
11041        && let Some(type_descriptor) = trailing.named_child(0)
11042    {
11043        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
11044        if !text.is_empty() {
11045            return Some(text);
11046        }
11047    }
11048
11049    let mut current = function_declarator;
11050    let mut indirection = String::new();
11051    while let Some(parent) = ancestry.parent(current) {
11052        if matches!(
11053            parent.kind(),
11054            "function_definition" | "declaration" | "field_declaration"
11055        ) {
11056            let type_node = parent.child_by_field_name("type")?;
11057            if cpp_export_macro_token(node_text(type_node, source))
11058                && (0..parent.named_child_count()).any(|index| {
11059                    parent
11060                        .named_child(index)
11061                        .is_some_and(|child| child.kind() == "ERROR")
11062                })
11063            {
11064                // Export/decorator macros commonly occupy the grammar's `type`
11065                // field and leave the semantic return type in an ERROR sibling.
11066                // Do not persist the macro token as a return type. The malformed
11067                // declaration does not carry enough structured evidence here.
11068                return None;
11069            }
11070            let base = normalize_cpp_whitespace(node_text(type_node, source));
11071            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
11072        }
11073        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11074            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
11075                && parent.named_child_count() == 1
11076                && parent.named_child(0) == Some(current));
11077        if wraps_current_declarator {
11078            match parent.kind() {
11079                "pointer_declarator" => indirection.push('*'),
11080                "reference_declarator" => {
11081                    let reference = parent
11082                        .children(&mut parent.walk())
11083                        .find(|child| !child.is_named())
11084                        .map(|child| node_text(child, source))
11085                        .unwrap_or("&");
11086                    indirection.push_str(reference);
11087                }
11088                "init_declarator" | "parenthesized_declarator" => {}
11089                _ => return None,
11090            }
11091            current = parent;
11092            continue;
11093        }
11094        return None;
11095    }
11096    None
11097}
11098
11099fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
11100    let mut required = 0;
11101    let mut total = 0;
11102    let mut repeated = false;
11103    let mut cursor = parameters_node.walk();
11104    for child in parameters_node.children(&mut cursor) {
11105        match child.kind() {
11106            "parameter_declaration" => {
11107                if cpp_parameter_is_explicit_object(child, source) {
11108                    continue;
11109                }
11110                if child.child_by_field_name("declarator").is_none()
11111                    && child
11112                        .child_by_field_name("type")
11113                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
11114                {
11115                    continue;
11116                }
11117                required += 1;
11118                total += 1;
11119            }
11120            "optional_parameter_declaration" => total += 1,
11121            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
11122                repeated = true;
11123            }
11124            _ => {}
11125        }
11126    }
11127    CallableArity::new(required, total, repeated)
11128}
11129
11130fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
11131    parameter
11132        .child_by_field_name("type")
11133        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
11134        .and_then(|type_node| type_node.child_by_field_name("constraint"))
11135        .is_some_and(|constraint| {
11136            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
11137        })
11138}
11139
11140/// One entry of a callable's invocation parameter list.
11141///
11142/// The list excludes an explicit object parameter and a lone `void`, so its
11143/// length is the callable's invocation arity. Every derivation of a parameter
11144/// type - the rendered spelling used for overload discrimination and the
11145/// structured identity used by dependency-pack production - starts from this
11146/// same sequence, so the two can never disagree about which parameters exist.
11147#[derive(Clone, Copy)]
11148enum CppParameterSlot<'tree> {
11149    Declared(Node<'tree>),
11150    Ellipsis,
11151}
11152
11153fn cpp_callable_parameter_slots<'tree>(
11154    parameters_node: Node<'tree>,
11155    source: &str,
11156) -> Vec<CppParameterSlot<'tree>> {
11157    let mut slots = Vec::new();
11158    let mut cursor = parameters_node.walk();
11159    for parameter in parameters_node.children(&mut cursor) {
11160        match parameter.kind() {
11161            "parameter_declaration" | "optional_parameter_declaration" => {
11162                if cpp_parameter_is_explicit_object(parameter, source)
11163                    || (parameter.child_by_field_name("declarator").is_none()
11164                        && parameter
11165                            .child_by_field_name("type")
11166                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
11167                {
11168                    continue;
11169                }
11170                slots.push(CppParameterSlot::Declared(parameter));
11171            }
11172            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
11173                slots.push(CppParameterSlot::Ellipsis);
11174            }
11175            _ => {}
11176        }
11177    }
11178    slots
11179}
11180
11181fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
11182    cpp_callable_parameter_slots(parameters_node, source)
11183        .into_iter()
11184        .map(|slot| match slot {
11185            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
11186            CppParameterSlot::Ellipsis => "...".to_string(),
11187        })
11188        .collect()
11189}
11190
11191/// One callable parameter's parser-derived type.
11192///
11193/// A rendered spelling such as `const T&` is a source text, not a type name. A
11194/// consumer that must publish a type into a structured model - a semantic-pack
11195/// type reference, for example - reads this instead.
11196#[derive(Debug, Clone, PartialEq, Eq)]
11197pub enum CppParameterType {
11198    /// The written type reduced to a structured identity. C++ cv-qualifiers
11199    /// have no place in that model and are not represented.
11200    Structured(StructuredTypeIdentity),
11201    /// A `...` pack, which declares no parameter type at all.
11202    Ellipsis,
11203    /// A written type with no structured reduction, such as a macro-obscured,
11204    /// `decltype`-computed, or function-pointer parameter.
11205    Unstructured,
11206}
11207
11208/// The structured type of each invocation parameter, in declaration order.
11209///
11210/// The result is index-parallel with the rendered
11211/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
11212/// callable.
11213pub fn cpp_callable_parameter_type_identities<'tree>(
11214    function_declarator: Node<'tree>,
11215    source: &str,
11216    ancestry: &ParentIndex<'tree>,
11217) -> Vec<CppParameterType> {
11218    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11219        return Vec::new();
11220    };
11221    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11222    cpp_callable_parameter_slots(parameters_node, source)
11223        .into_iter()
11224        .map(|slot| match slot {
11225            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
11226            CppParameterSlot::Declared(parameter) => {
11227                cpp_parameter_type_identity(parameter, source, &lexical_scope)
11228                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
11229            }
11230        })
11231        .collect()
11232}
11233
11234/// The parser-derived type of one declared object or parameter.
11235///
11236/// The declaration owns the base `type` field while the individual declarator
11237/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
11238/// lets callers distinguish several declarators in one declaration without
11239/// reparsing a rendered signature.
11240pub fn cpp_declaration_type_identity<'tree>(
11241    declaration: Node<'tree>,
11242    declarator: Node<'tree>,
11243    source: &str,
11244    ancestry: &ParentIndex<'tree>,
11245) -> Option<StructuredTypeIdentity> {
11246    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
11247    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
11248}
11249
11250fn cpp_parameter_type_identity(
11251    parameter: Node<'_>,
11252    source: &str,
11253    lexical_scope: &[String],
11254) -> Option<StructuredTypeIdentity> {
11255    cpp_declaration_type_identity_in_scope(
11256        parameter,
11257        cpp_parameter_declarator(parameter),
11258        source,
11259        lexical_scope,
11260    )
11261}
11262
11263fn cpp_declaration_type_identity_in_scope(
11264    declaration: Node<'_>,
11265    declarator: Option<Node<'_>>,
11266    source: &str,
11267    lexical_scope: &[String],
11268) -> Option<StructuredTypeIdentity> {
11269    let type_node = declaration.child_by_field_name("type")?;
11270    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
11271    if let Some(declarator) = declarator {
11272        for wrapper in cpp_structured_declarator_wrappers(declarator)?
11273            .into_iter()
11274            .rev()
11275        {
11276            identity = cpp_wrap_structured_type(identity, wrapper)?;
11277        }
11278    }
11279    Some(identity)
11280}
11281
11282/// One callable parameter's comparable shape.
11283///
11284/// [`CppParameterType`] above answers "which type is written here" for a
11285/// structured model and deliberately records no cv-qualifiers, so it reports
11286/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
11287/// callable declarations declare one function needs the opposite trade: every
11288/// cv-qualifier that C++ counts as part of the parameter type must survive,
11289/// while the two declarations may spell the same type through different
11290/// qualifications. This slot carries that comparand.
11291///
11292/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
11293/// and with the rendered parameter spellings of the same callable.
11294#[derive(Debug, Clone, PartialEq, Eq)]
11295pub enum CppComparableSlot {
11296    /// A declared parameter reduced to its comparable shape.
11297    Shape(CppComparableParameter),
11298    /// A `...` pack, which declares no parameter type at all.
11299    Ellipsis,
11300    /// A parameter with no comparable reduction, such as a macro-obscured,
11301    /// `decltype`-computed, or function-pointer parameter.
11302    Unstructured,
11303}
11304
11305/// A parameter type as a flat arena of nodes plus a root index.
11306///
11307/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
11308/// can nest types very deeply, and cloning, comparing or dropping the value
11309/// must not consume the Rust call stack. Nodes are appended in post-order, so
11310/// every child index is smaller than its parent's and the last appended node is
11311/// the root.
11312///
11313/// That post-order append is also what makes the derived `PartialEq` a correct
11314/// structural equality: the builder below is deterministic, so one type shape
11315/// has exactly one arena layout no matter which spelling produced it. Two
11316/// shapes are equal as values iff they are equal as type trees.
11317#[derive(Debug, Clone, PartialEq, Eq)]
11318pub struct CppComparableParameter {
11319    nodes: Vec<CppComparableNode>,
11320    root: usize,
11321}
11322
11323/// One node of a [`CppComparableParameter`] arena.
11324///
11325/// `Reference` and `Array` carry no qualifiers because the grammar writes none
11326/// on them: a reference cannot be cv-qualified in C++, and an array's
11327/// qualifiers belong to its element type. A cv-qualifier written on a generic
11328/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
11329/// which is the only Named node the whole spelling produces.
11330#[derive(Debug, Clone, PartialEq, Eq)]
11331pub enum CppComparableNode {
11332    Named {
11333        name: StructuredTypeName,
11334        primitive: bool,
11335        konst: bool,
11336        volatil: bool,
11337    },
11338    Pointer {
11339        inner: usize,
11340        konst: bool,
11341        volatil: bool,
11342    },
11343    Reference {
11344        inner: usize,
11345    },
11346    Array {
11347        inner: usize,
11348    },
11349    Generic {
11350        base: usize,
11351        arguments: Vec<usize>,
11352    },
11353}
11354
11355impl CppComparableParameter {
11356    pub fn root(&self) -> usize {
11357        self.root
11358    }
11359
11360    pub fn node(&self, index: usize) -> &CppComparableNode {
11361        &self.nodes[index]
11362    }
11363
11364    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
11365    /// parameter's top level only.
11366    ///
11367    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
11368    /// declare one function, and a top-level array type becomes a pointer to
11369    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
11370    /// type constructor is this arena's root, which is why both adjustments
11371    /// are one match on it: cv on an inner pointer level, on a pointee, or on
11372    /// an array element keeps distinguishing the type, and an array behind a
11373    /// pointer or reference is not a top-level array.
11374    fn adjust_parameter_top_level(&mut self) {
11375        let root = self.root;
11376        match &mut self.nodes[root] {
11377            CppComparableNode::Named { konst, volatil, .. }
11378            | CppComparableNode::Pointer { konst, volatil, .. } => {
11379                *konst = false;
11380                *volatil = false;
11381            }
11382            CppComparableNode::Array { inner } => {
11383                let inner = *inner;
11384                self.nodes[root] = CppComparableNode::Pointer {
11385                    inner,
11386                    konst: false,
11387                    volatil: false,
11388                };
11389            }
11390            CppComparableNode::Generic { base, .. } => {
11391                let base = *base;
11392                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
11393                    unreachable!("a comparable generic's base is always a named leaf");
11394                };
11395                *konst = false;
11396                *volatil = false;
11397            }
11398            CppComparableNode::Reference { .. } => {}
11399        }
11400    }
11401}
11402
11403/// The comparable shape of each invocation parameter, in declaration order.
11404///
11405/// The result is index-parallel with
11406/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
11407/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
11408/// must treat as evidence of nothing rather than as agreement.
11409pub fn cpp_comparable_parameter_shapes<'tree>(
11410    function_declarator: Node<'tree>,
11411    source: &str,
11412    ancestry: &ParentIndex<'tree>,
11413) -> Vec<CppComparableSlot> {
11414    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11415        return Vec::new();
11416    };
11417    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11418    cpp_callable_parameter_slots(parameters_node, source)
11419        .into_iter()
11420        .map(|slot| match slot {
11421            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
11422            CppParameterSlot::Declared(parameter) => {
11423                cpp_comparable_parameter(parameter, source, &lexical_scope)
11424                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
11425            }
11426        })
11427        .collect()
11428}
11429
11430fn cpp_comparable_parameter(
11431    parameter: Node<'_>,
11432    source: &str,
11433    lexical_scope: &[String],
11434) -> Option<CppComparableParameter> {
11435    let type_node = parameter.child_by_field_name("type")?;
11436    let levels = match cpp_parameter_declarator(parameter) {
11437        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
11438        None => Vec::new(),
11439    };
11440    let mut shape = cpp_comparable_type_shape(
11441        type_node,
11442        cpp_cv_qualifiers(parameter, source),
11443        levels,
11444        source,
11445        lexical_scope,
11446    )?;
11447    shape.adjust_parameter_top_level();
11448    Some(shape)
11449}
11450
11451/// The `const` and `volatile` qualifiers written as direct named children of
11452/// `node`.
11453///
11454/// The grammar exposes `type_qualifier` as a non-field named child in exactly
11455/// the three places a parameter's qualifiers can be written: on the
11456/// `parameter_declaration` itself (the base type), on a `type_descriptor`
11457/// (inside a template argument list), and on each `pointer_declarator` level
11458/// (the pointer object). Every other qualifier the grammar admits - `restrict`
11459/// and friends - takes no part in C++ type identity, the same filter
11460/// `cpp_parameter_type` applies to the rendered spelling (#1827).
11461fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
11462    let mut qualifiers = CppCvQualifiers::default();
11463    let mut cursor = node.walk();
11464    for child in node.named_children(&mut cursor) {
11465        if child.kind() != "type_qualifier" {
11466            continue;
11467        }
11468        match node_text(child, source) {
11469            "const" => qualifiers.konst = true,
11470            "volatile" => qualifiers.volatil = true,
11471            _ => {}
11472        }
11473    }
11474    qualifiers
11475}
11476
11477#[derive(Clone, Copy, Default)]
11478struct CppCvQualifiers {
11479    konst: bool,
11480    volatil: bool,
11481}
11482
11483impl CppCvQualifiers {
11484    fn union(self, other: Self) -> Self {
11485        Self {
11486            konst: self.konst || other.konst,
11487            volatil: self.volatil || other.volatil,
11488        }
11489    }
11490}
11491
11492/// One pointer, reference or array level a declarator chain adds.
11493#[derive(Clone, Copy)]
11494enum CppComparableLevel {
11495    Pointer { konst: bool, volatil: bool },
11496    Reference,
11497    Array,
11498}
11499
11500/// The levels `declarator` adds, outermost written level first.
11501///
11502/// C++ declarator syntax binds inside out: the level written closest to the
11503/// declared name is the outermost type constructor, and tree-sitter nests it
11504/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
11505/// applies in order to reach "array of pointer to int", and the qualifier of
11506/// `int * const *p` is read on the level it was written next to, the inner
11507/// pointer of the resulting type.
11508///
11509/// A declarator chain that names a function type - a function-pointer
11510/// parameter - has no comparable shape and reports `None`, matching the
11511/// structured identity channel.
11512fn cpp_comparable_declarator_levels(
11513    declarator: Node<'_>,
11514    source: &str,
11515) -> Option<Vec<CppComparableLevel>> {
11516    let mut levels = Vec::new();
11517    let mut current = declarator;
11518    loop {
11519        match current.kind() {
11520            "pointer_declarator" | "abstract_pointer_declarator" => {
11521                let qualifiers = cpp_cv_qualifiers(current, source);
11522                levels.push(CppComparableLevel::Pointer {
11523                    konst: qualifiers.konst,
11524                    volatil: qualifiers.volatil,
11525                });
11526            }
11527            "reference_declarator" | "abstract_reference_declarator" => {
11528                levels.push(CppComparableLevel::Reference);
11529            }
11530            "array_declarator" | "abstract_array_declarator" => {
11531                levels.push(CppComparableLevel::Array);
11532            }
11533            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
11534            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
11535            _ => return None,
11536        }
11537        let Some(next) = cpp_nested_declarator(current) else {
11538            return Some(levels);
11539        };
11540        current = next;
11541    }
11542}
11543
11544/// Reduce one written type to a comparable arena.
11545///
11546/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
11547/// two additions: each visited type node carries the cv-qualifiers written on
11548/// it, and declarator levels arrive as a prepared list rather than being
11549/// rediscovered inside the walk.
11550fn cpp_comparable_type_shape(
11551    type_node: Node<'_>,
11552    qualifiers: CppCvQualifiers,
11553    levels: Vec<CppComparableLevel>,
11554    source: &str,
11555    lexical_scope: &[String],
11556) -> Option<CppComparableParameter> {
11557    enum Work<'tree> {
11558        Visit {
11559            node: Node<'tree>,
11560            qualifiers: CppCvQualifiers,
11561        },
11562        ApplyLevels(Vec<CppComparableLevel>),
11563        BuildGeneric {
11564            argument_count: usize,
11565        },
11566    }
11567
11568    let mut nodes: Vec<CppComparableNode> = Vec::new();
11569    let mut values: Vec<usize> = Vec::new();
11570    let mut work = vec![
11571        Work::ApplyLevels(levels),
11572        Work::Visit {
11573            node: type_node,
11574            qualifiers,
11575        },
11576    ];
11577    while let Some(next) = work.pop() {
11578        match next {
11579            Work::Visit { node, qualifiers } => match node.kind() {
11580                "type_descriptor" => {
11581                    let inner_type = node
11582                        .child_by_field_name("type")
11583                        .or_else(|| node.named_child(0))?;
11584                    let mut cursor = node.walk();
11585                    let declarator = node.child_by_field_name("declarator").or_else(|| {
11586                        node.named_children(&mut cursor).find(|child| {
11587                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
11588                        })
11589                    });
11590                    let levels = match declarator {
11591                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
11592                        None => Vec::new(),
11593                    };
11594                    work.push(Work::ApplyLevels(levels));
11595                    work.push(Work::Visit {
11596                        node: inner_type,
11597                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
11598                    });
11599                }
11600                "sized_type_specifier" => {
11601                    // `unsigned char` is one primitive type whose components are
11602                    // partly unnamed tokens, so the whole specifier is its own
11603                    // name component. Reducing it to the `type` child would make
11604                    // `f(unsigned char)` and `f(char)` compare equal.
11605                    let name = StructuredTypeName::new(
11606                        vec![normalize_cpp_whitespace(node_text(node, source))],
11607                        lexical_scope.to_vec(),
11608                        false,
11609                    )?;
11610                    values.push(cpp_push_comparable_node(
11611                        &mut nodes,
11612                        CppComparableNode::Named {
11613                            name,
11614                            primitive: true,
11615                            konst: qualifiers.konst,
11616                            volatil: qualifiers.volatil,
11617                        },
11618                    ));
11619                }
11620                "qualified_identifier"
11621                | "scoped_identifier"
11622                | "scoped_type_identifier"
11623                | "type_identifier"
11624                | "field_identifier"
11625                | "identifier"
11626                | "namespace_identifier"
11627                | "primitive_type"
11628                | "template_type" => {
11629                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
11630                    values.push(cpp_push_comparable_node(
11631                        &mut nodes,
11632                        CppComparableNode::Named {
11633                            name,
11634                            primitive: node.kind() == "primitive_type",
11635                            konst: qualifiers.konst,
11636                            volatil: qualifiers.volatil,
11637                        },
11638                    ));
11639                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
11640                        let mut cursor = arguments_node.walk();
11641                        let arguments = arguments_node
11642                            .named_children(&mut cursor)
11643                            .filter(|child| !child.is_extra() && child.kind() != "comment")
11644                            .collect::<Vec<_>>();
11645                        work.push(Work::BuildGeneric {
11646                            argument_count: arguments.len(),
11647                        });
11648                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
11649                            node: argument,
11650                            qualifiers: CppCvQualifiers::default(),
11651                        }));
11652                    }
11653                }
11654                _ => {
11655                    let inner = node.child_by_field_name("type").or_else(|| {
11656                        (node.named_child_count() == 1)
11657                            .then(|| node.named_child(0))
11658                            .flatten()
11659                    })?;
11660                    work.push(Work::Visit {
11661                        node: inner,
11662                        qualifiers,
11663                    });
11664                }
11665            },
11666            Work::ApplyLevels(levels) => {
11667                let mut root = values.pop()?;
11668                for level in levels {
11669                    let node = match level {
11670                        CppComparableLevel::Pointer { konst, volatil } => {
11671                            CppComparableNode::Pointer {
11672                                inner: root,
11673                                konst,
11674                                volatil,
11675                            }
11676                        }
11677                        CppComparableLevel::Reference => {
11678                            CppComparableNode::Reference { inner: root }
11679                        }
11680                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
11681                    };
11682                    root = cpp_push_comparable_node(&mut nodes, node);
11683                }
11684                values.push(root);
11685            }
11686            Work::BuildGeneric { argument_count } => {
11687                let value_count = argument_count.checked_add(1)?;
11688                let start = values.len().checked_sub(value_count)?;
11689                let mut built = values.split_off(start);
11690                let base = built.remove(0);
11691                values.push(cpp_push_comparable_node(
11692                    &mut nodes,
11693                    CppComparableNode::Generic {
11694                        base,
11695                        arguments: built,
11696                    },
11697                ));
11698            }
11699        }
11700    }
11701    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
11702    debug_assert_eq!(
11703        root,
11704        nodes.len().saturating_sub(1),
11705        "comparable nodes are appended in post-order, so the root is the last one"
11706    );
11707    Some(CppComparableParameter { nodes, root })
11708}
11709
11710fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
11711    nodes.push(node);
11712    nodes.len() - 1
11713}
11714
11715/// The template argument list of the name `node` terminates in, if any.
11716///
11717/// `std::vector<int>` writes its arguments on the `name` of a qualified
11718/// identifier, so a walk that stopped at the qualified node would reduce
11719/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
11720fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
11721    let mut current = node;
11722    loop {
11723        match current.kind() {
11724            "template_type" => return current.child_by_field_name("arguments"),
11725            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11726                current = current.child_by_field_name("name")?;
11727            }
11728            _ => return None,
11729        }
11730    }
11731}
11732
11733/// The callable declarator of the declaration that covers `start_byte`.
11734///
11735/// A consumer that holds a declaration's recorded byte position rather than its
11736/// syntax node - external header extraction, for instance - uses this to reach
11737/// the same `function_declarator` the declaration walk read.
11738pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
11739    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
11740    loop {
11741        if matches!(
11742            current.kind(),
11743            "declaration" | "field_declaration" | "function_definition"
11744        ) && let Some(declarator) = current
11745            .child_by_field_name("declarator")
11746            .and_then(extract_function_declarator)
11747        {
11748            return Some(declarator);
11749        }
11750        current = current.parent()?;
11751    }
11752}
11753
11754fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
11755    let mut labels = Vec::new();
11756    let mut cursor = parameters_node.walk();
11757    for child in parameters_node.children(&mut cursor) {
11758        match child.kind() {
11759            "parameter_declaration" | "optional_parameter_declaration" => {
11760                if let Some(name_node) = child
11761                    .child_by_field_name("declarator")
11762                    .and_then(cpp_declarator_label_node)
11763                {
11764                    labels.push(name_node);
11765                } else {
11766                    labels.push(child);
11767                }
11768            }
11769            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
11770                labels.push(child);
11771            }
11772            _ => {}
11773        }
11774    }
11775    labels
11776}
11777
11778fn cpp_signature_search_start<'tree>(
11779    signature: &str,
11780    function_declarator: Node<'tree>,
11781    source: &str,
11782    ancestry: &ParentIndex<'tree>,
11783) -> usize {
11784    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
11785        return 0;
11786    };
11787    let raw = node_text(enclosing, source);
11788    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
11789    let offset = function_declarator
11790        .start_byte()
11791        .saturating_sub(enclosing.start_byte())
11792        .saturating_sub(leading_trim_bytes);
11793    offset.min(signature.len())
11794}
11795
11796fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
11797    match node.kind() {
11798        "identifier" | "field_identifier" => Some(node),
11799        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
11800            .child_by_field_name("declarator")
11801            .or_else(|| last_named_child(node))
11802            .and_then(cpp_declarator_label_node),
11803        "array_declarator" => node
11804            .child_by_field_name("declarator")
11805            .and_then(cpp_declarator_label_node),
11806        "function_declarator" => node
11807            .child_by_field_name("declarator")
11808            .or_else(|| node.child_by_field_name("name"))
11809            .or_else(|| last_named_child(node))
11810            .and_then(cpp_declarator_label_node),
11811        _ => None,
11812    }
11813}
11814
11815fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
11816    let base_type = parameter
11817        .child_by_field_name("type")
11818        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
11819        .unwrap_or_default();
11820    let declarator = cpp_parameter_declarator(parameter);
11821    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
11822    // are discarded, so `f(const int)` and `f(int)` declare one function. A
11823    // qualifier written next to the parameter's type is only top-level when
11824    // the declarator adds no indirection; behind a pointer, reference or array
11825    // declarator the same qualifier belongs to the pointee, referent or
11826    // element and keeps distinguishing the type (#1827).
11827    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
11828    let mut cursor = parameter.walk();
11829    let qualifiers = parameter
11830        .named_children(&mut cursor)
11831        .filter(|child| child.kind() == "type_qualifier")
11832        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
11833        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
11834        .collect::<Vec<_>>()
11835        .join(" ");
11836    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
11837        (true, _) => base_type,
11838        (_, true) => qualifiers,
11839        (false, false) => format!("{qualifiers} {base_type}"),
11840    };
11841    let declarator_suffix = declarator
11842        .map(|node| cpp_declarator_suffix_without_name(node, source))
11843        .unwrap_or_default();
11844
11845    let combined = if type_text.is_empty() {
11846        declarator_suffix
11847    } else if declarator_suffix.is_empty() {
11848        type_text
11849    } else {
11850        format!("{type_text} {declarator_suffix}")
11851    };
11852    normalize_cpp_type_text(&combined)
11853}
11854
11855fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
11856    parameter.child_by_field_name("declarator").or_else(|| {
11857        // Some unnamed prototype parameters expose their abstract declarator
11858        // as a direct named child without the grammar's `declarator` field.
11859        // Recover only the structured abstract-declarator node; the parameter's
11860        // type and qualifiers are distinct children and must not be guessed from
11861        // source text.
11862        let mut cursor = parameter.walk();
11863        parameter
11864            .named_children(&mut cursor)
11865            .find(|child| is_cpp_abstract_declarator(child.kind()))
11866    })
11867}
11868
11869/// Whether a parameter's declarator chain adds indirection - a pointer,
11870/// reference, array or function declarator - to the parameter's written type.
11871pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
11872    let mut current = Some(declarator);
11873    while let Some(node) = current {
11874        if matches!(
11875            node.kind(),
11876            "pointer_declarator"
11877                | "abstract_pointer_declarator"
11878                | "reference_declarator"
11879                | "abstract_reference_declarator"
11880                | "array_declarator"
11881                | "abstract_array_declarator"
11882                | "function_declarator"
11883                | "abstract_function_declarator"
11884        ) {
11885            return true;
11886        }
11887        current = cpp_nested_declarator(node);
11888    }
11889    false
11890}
11891
11892fn is_cpp_abstract_declarator(kind: &str) -> bool {
11893    matches!(
11894        kind,
11895        "abstract_pointer_declarator"
11896            | "abstract_reference_declarator"
11897            | "abstract_array_declarator"
11898            | "abstract_function_declarator"
11899            | "abstract_parenthesized_declarator"
11900    )
11901}
11902
11903fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
11904    node.child_by_field_name("declarator").or_else(|| {
11905        if is_cpp_abstract_declarator(node.kind()) {
11906            let mut cursor = node.walk();
11907            node.named_children(&mut cursor)
11908                .find(|child| is_cpp_abstract_declarator(child.kind()))
11909        } else {
11910            // Named declarators historically use their last named child when
11911            // tree-sitter omits the field. Keep that broad fallback for
11912            // attributed, variadic, and recovered named shapes.
11913            last_named_child(node)
11914        }
11915    })
11916}
11917
11918fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
11919    match node.kind() {
11920        "identifier" | "field_identifier" => String::new(),
11921        "pointer_declarator" | "abstract_pointer_declarator" => {
11922            let inner = cpp_nested_declarator(node)
11923                .map(|child| cpp_declarator_suffix_without_name(child, source))
11924                .unwrap_or_default();
11925            format!("*{inner}")
11926        }
11927        "reference_declarator" | "abstract_reference_declarator" => {
11928            let inner = cpp_nested_declarator(node)
11929                .map(|child| cpp_declarator_suffix_without_name(child, source))
11930                .unwrap_or_default();
11931            let reference = node
11932                .children(&mut node.walk())
11933                .find(|child| matches!(child.kind(), "&" | "&&"))
11934                .map(|child| node_text(child, source))
11935                .unwrap_or("&");
11936            format!("{reference}{inner}")
11937        }
11938        "array_declarator" | "abstract_array_declarator" => {
11939            let inner = cpp_nested_declarator(node)
11940                .map(|child| cpp_declarator_suffix_without_name(child, source))
11941                .unwrap_or_default();
11942            let size = node
11943                .child_by_field_name("size")
11944                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
11945                .unwrap_or_default();
11946            format!("{inner}[{size}]")
11947        }
11948        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
11949            let inner = cpp_nested_declarator(node);
11950            inner
11951                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
11952                .unwrap_or_default()
11953        }
11954        "function_declarator" | "abstract_function_declarator" => {
11955            let inner = cpp_nested_declarator(node)
11956                .map(|child| cpp_declarator_suffix_without_name(child, source))
11957                .unwrap_or_default();
11958            let params = node
11959                .child_by_field_name("parameters")
11960                .map(|child| cpp_parameter_signature(child, source))
11961                .unwrap_or_else(|| "()".to_string());
11962            format!("{inner}{params}")
11963        }
11964        _ => {
11965            let text = normalize_cpp_whitespace(node_text(node, source));
11966            let name = extract_declarator_name(node, source);
11967            if name.is_empty() {
11968                text
11969            } else {
11970                text.replace(&name, "").trim().to_string()
11971            }
11972        }
11973    }
11974}
11975
11976fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
11977    collapse_cpp_whitespace(
11978        suffix
11979            .trim()
11980            .trim_start_matches("->")
11981            .trim_start_matches('{')
11982            .trim_end_matches(';'),
11983    )
11984}
11985
11986pub fn normalize_cpp_whitespace(value: &str) -> String {
11987    collapse_cpp_whitespace(value)
11988}
11989
11990fn normalize_cpp_type_text(value: &str) -> String {
11991    collapse_cpp_whitespace(value)
11992        .replace(", ", ",")
11993        .replace(" <", "<")
11994        .replace("< ", "<")
11995        .replace(" >", ">")
11996}
11997
11998fn collapse_cpp_whitespace(value: &str) -> String {
11999    let mut result = String::new();
12000    let mut prev_space = false;
12001    for ch in value.chars() {
12002        if ch.is_whitespace() {
12003            if !prev_space {
12004                result.push(' ');
12005            }
12006            prev_space = true;
12007        } else {
12008            result.push(ch);
12009            prev_space = false;
12010        }
12011    }
12012    result.trim().to_string()
12013}
12014
12015pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
12016    node_source_text(node, source)
12017}
12018
12019pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
12020    walk_named_tree_preorder(node, true, |node| {
12021        match node.kind() {
12022            "type_identifier" | "identifier" | "qualified_identifier" => {
12023                let text = node_text(node, source).trim();
12024                if !text.is_empty() {
12025                    identifiers.insert(text.to_string());
12026                }
12027            }
12028            _ => {}
12029        }
12030        WalkControl::Continue
12031    });
12032}
12033
12034fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
12035    node.child_by_field_name("body").or_else(|| {
12036        let mut cursor = node.walk();
12037        node.named_children(&mut cursor).find(|child| {
12038            matches!(
12039                child.kind(),
12040                "declaration_list" | "field_declaration_list" | "enumerator_list"
12041            )
12042        })
12043    })
12044}
12045
12046/// Return a class body's actual closing brace when the parser supplied one.
12047///
12048/// A malformed namespace sentinel can leave a class node carrying unrelated
12049/// parser errors even though its own class body is complete.  `has_error()` is
12050/// therefore too coarse an admission predicate for sentinel ownership.  The
12051/// body list, however, exposes the opening and closing punctuation directly;
12052/// a real (non-missing) final `}` proves that the class did not borrow the
12053/// enclosing namespace's close.  Requiring the body to end before its parent
12054/// container also rejects a recovered node whose body swallowed that outer
12055/// boundary.
12056fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
12057    if !matches!(
12058        node.kind(),
12059        "class_specifier" | "struct_specifier" | "union_specifier"
12060    ) {
12061        return None;
12062    }
12063    let body = cpp_body_node(node)?;
12064    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
12065        return None;
12066    }
12067    let open = body.child(0)?;
12068    let close = body.child(body.child_count().checked_sub(1)?)?;
12069    if open.kind() != "{"
12070        || open.is_missing()
12071        || close.kind() != "}"
12072        || close.is_missing()
12073        || close.end_byte() != body.end_byte()
12074        || body.end_byte() > node.end_byte()
12075        || node
12076            .parent()
12077            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
12078    {
12079        return None;
12080    }
12081    Some(close)
12082}
12083
12084fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
12085    if node.kind() == "namespace_definition" {
12086        return true;
12087    }
12088    let mut cursor = node.walk();
12089    node.named_children(&mut cursor)
12090        .any(cpp_contains_namespace_definition)
12091}
12092
12093struct CppNestedNamespaceSentinel<'tree> {
12094    function: Node<'tree>,
12095    body: Node<'tree>,
12096    namespace_components: Vec<String>,
12097}
12098
12099/// Owned structural recovery metadata for a namespace-sentinel region.
12100///
12101/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
12102/// instead of the namespace/class scopes that the declaration visitor restores.
12103/// The inverted usage walk has the original CST, so it needs the same ownership
12104/// evidence without borrowing parser nodes across its file scan.  Keep this
12105/// descriptor deliberately source-range based: callers can match a reference
12106/// node by containment and then resolve its structured type spelling in the
12107/// recovered class scope.
12108#[derive(Debug, Clone)]
12109pub struct CppSentinelRecoveredOwner {
12110    pub range: Range,
12111    /// Start of the qualified owner name (`btree<P>::method`).  A leading
12112    /// return type before this byte is looked up from the namespace; parameters,
12113    /// trailing returns, and the body use the member owner scope.
12114    pub owner_name_start_byte: usize,
12115    /// Number of leading components belonging to the namespace rather than
12116    /// the qualified class owner.  A leading return type is looked up before
12117    /// every owner component, not merely before the innermost class.
12118    pub namespace_component_count: usize,
12119    pub scope_components: Vec<String>,
12120}
12121
12122#[derive(Debug, Clone)]
12123pub struct CppSentinelRecoveredClass {
12124    pub namespace_range: Range,
12125    pub namespace_scope_components: Vec<String>,
12126    pub class_range: Range,
12127    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
12128    pub scope_components: Vec<String>,
12129    /// Qualified out-of-line member definitions owned by this class.  Their
12130    /// ranges may extend beyond `class_range` when the malformed sentinel
12131    /// swallowed the namespace close and left definitions as function siblings.
12132    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
12133}
12134
12135/// Resolve the lexical scope restored for a node in a malformed
12136/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
12137/// outrank class spans, which in turn outrank the surviving namespace body.
12138/// The class ancestor suffix is recovered from the original CST so nested
12139/// members keep their complete `Outer::Inner` owner chain.
12140pub fn cpp_sentinel_recovered_scope_for_node(
12141    node: Node<'_>,
12142    source: &str,
12143    recovered_classes: &[CppSentinelRecoveredClass],
12144) -> Option<Vec<String>> {
12145    let contains =
12146        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
12147    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
12148    for recovered in recovered_classes {
12149        for owner in recovered
12150            .owner_ranges
12151            .iter()
12152            .filter(|owner| contains(owner.range))
12153        {
12154            let replace = best_owner.is_none_or(|existing| {
12155                owner.range.end_byte.saturating_sub(owner.range.start_byte)
12156                    < existing
12157                        .range
12158                        .end_byte
12159                        .saturating_sub(existing.range.start_byte)
12160            });
12161            if replace {
12162                best_owner = Some(owner);
12163            }
12164        }
12165    }
12166    if let Some(owner) = best_owner {
12167        let mut scope = owner.scope_components.clone();
12168        if node.start_byte() < owner.owner_name_start_byte {
12169            scope.truncate(owner.namespace_component_count);
12170        }
12171        return Some(scope);
12172    }
12173
12174    let class = recovered_classes
12175        .iter()
12176        .filter(|recovered| contains(recovered.class_range))
12177        .min_by_key(|recovered| {
12178            recovered
12179                .class_range
12180                .end_byte
12181                .saturating_sub(recovered.class_range.start_byte)
12182        });
12183    let class_scope = class.is_some();
12184    let mut scope = if let Some(class) = class {
12185        class.scope_components.clone()
12186    } else {
12187        let namespace = recovered_classes
12188            .iter()
12189            .filter(|recovered| contains(recovered.namespace_range))
12190            .min_by_key(|recovered| {
12191                recovered
12192                    .namespace_range
12193                    .end_byte
12194                    .saturating_sub(recovered.namespace_range.start_byte)
12195            })?;
12196        let mut scope = namespace.namespace_scope_components.clone();
12197        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
12198        let common_prefix = scope
12199            .iter()
12200            .zip(&parser_namespace)
12201            .take_while(|(recovered, parser)| recovered == parser)
12202            .count();
12203        scope.extend(parser_namespace.into_iter().skip(common_prefix));
12204        scope
12205    };
12206    if class_scope {
12207        let mut ancestor_components = Vec::new();
12208        let mut ancestor = node.parent();
12209        while let Some(current) = ancestor {
12210            if matches!(
12211                current.kind(),
12212                "class_specifier" | "struct_specifier" | "union_specifier"
12213            ) && let Some(name) = current.child_by_field_name("name")
12214                && let Some(name_components) = cpp_name_components(name, source)
12215            {
12216                ancestor_components.push(
12217                    name_components
12218                        .into_iter()
12219                        .map(|component| component.name)
12220                        .collect::<Vec<_>>(),
12221                );
12222            }
12223            ancestor = current.parent();
12224        }
12225        ancestor_components.reverse();
12226        let base_len = scope.len();
12227        for component in ancestor_components.into_iter().flatten() {
12228            if scope.len() >= base_len && scope.last() == Some(&component) {
12229                continue;
12230            }
12231            scope.push(component);
12232        }
12233    }
12234    Some(scope)
12235}
12236
12237struct CppSentinelFragmentedClassTail<'tree> {
12238    class_node: Node<'tree>,
12239    template_node: Option<Node<'tree>>,
12240    name: String,
12241    raw_supertypes: Option<Vec<String>>,
12242    fragmented: FragmentedExportBody,
12243    consumed_start: usize,
12244}
12245
12246struct CppSentinelFragmentedClassErrorPrefix<'tree> {
12247    name: String,
12248    open: Node<'tree>,
12249    raw_supertypes: Option<Vec<String>>,
12250}
12251
12252struct CppSentinelDirectBodyClassRegion {
12253    namespace_components: Vec<String>,
12254    class_start: usize,
12255    class_start_line: usize,
12256    class_close_end: usize,
12257    class_close_line: usize,
12258    name: String,
12259}
12260
12261fn cpp_sentinel_body_class_candidate<'tree>(
12262    child: Node<'tree>,
12263) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
12264    if matches!(
12265        child.kind(),
12266        "class_specifier" | "struct_specifier" | "union_specifier"
12267    ) {
12268        return Some((child, None));
12269    }
12270    if child.kind() != "template_declaration" {
12271        if child.kind() == "declaration" {
12272            return Some((first_class_like_child(child)?, None));
12273        }
12274        return None;
12275    }
12276    let mut cursor = child.walk();
12277    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
12278        if matches!(
12279            candidate.kind(),
12280            "class_specifier" | "struct_specifier" | "union_specifier"
12281        ) {
12282            Some(candidate)
12283        } else if candidate.kind() == "declaration" {
12284            first_class_like_child(candidate)
12285        } else {
12286            None
12287        }
12288    })?;
12289    Some((class_node, Some(child)))
12290}
12291
12292/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
12293/// namespace-sentinel body when a later member macro ends the bogus sentinel
12294/// function before the real class close. The anonymous class/open tokens and
12295/// direct identifier are the structural proof; a retained direct close would
12296/// be an ordinary malformed class rather than the fragmented tail handled here.
12297fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
12298    node: Node<'tree>,
12299    source: &str,
12300) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
12301    let name = malformed_class_error_owner_name(node, source)?;
12302    let mut cursor = node.walk();
12303    let children = node.children(&mut cursor).collect::<Vec<_>>();
12304    let keyword = children.first()?;
12305    let open_index = children.iter().position(|child| child.kind() == "{")?;
12306    if children[open_index + 1..]
12307        .iter()
12308        .any(|child| child.kind() == "}")
12309    {
12310        return None;
12311    }
12312    let raw_supertypes =
12313        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
12314    Some(CppSentinelFragmentedClassErrorPrefix {
12315        name,
12316        open: children[open_index],
12317        raw_supertypes,
12318    })
12319}
12320
12321fn cpp_sentinel_direct_body_class_candidate<'tree>(
12322    child: Node<'tree>,
12323) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
12324    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
12325        return Some(candidate);
12326    }
12327    if child.kind() != "template_declaration" {
12328        return None;
12329    }
12330    let mut cursor = child.walk();
12331    let wrapper = child
12332        .named_children(&mut cursor)
12333        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
12334    Some((first_class_like_child(wrapper)?, Some(child)))
12335}
12336
12337fn cpp_sentinel_direct_namespace_components(
12338    function: Node<'_>,
12339    body: Node<'_>,
12340    source: &str,
12341) -> Option<Vec<String>> {
12342    let mut cursor = function.walk();
12343    let children = function
12344        .named_children(&mut cursor)
12345        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
12346        .collect::<Vec<_>>();
12347    let sentinel_index = children.iter().rposition(|child| {
12348        direct_identifier_name(*child, source)
12349            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
12350    })?;
12351    let mut identifiers = Vec::new();
12352    let mut stack = children[sentinel_index + 1..]
12353        .iter()
12354        .rev()
12355        .copied()
12356        .collect::<Vec<_>>();
12357    while let Some(current) = stack.pop() {
12358        if let Some(name) = direct_identifier_name(current, source) {
12359            identifiers.push(name);
12360            continue;
12361        }
12362        let mut cursor = current.walk();
12363        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
12364        stack.extend(children.into_iter().rev());
12365    }
12366    let [keyword, namespace] = identifiers.as_slice() else {
12367        return None;
12368    };
12369    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
12370        .then(|| vec![namespace.clone()])
12371}
12372
12373fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
12374    let mut sibling = class_semicolon.next_named_sibling();
12375    let namespace_close = loop {
12376        let Some(current) = sibling else {
12377            return false;
12378        };
12379        sibling = current.next_named_sibling();
12380        if current.kind() != "comment" {
12381            break current;
12382        }
12383    };
12384    if !cpp_is_stray_close_brace(namespace_close, source) {
12385        return false;
12386    }
12387    loop {
12388        let Some(current) = sibling else {
12389            return false;
12390        };
12391        sibling = current.next_named_sibling();
12392        if current.kind() == "comment" {
12393            continue;
12394        }
12395        return direct_identifier_name(current, source)
12396            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
12397    }
12398}
12399
12400fn cpp_sentinel_macro_body_class_region<'tree>(
12401    node: Node<'tree>,
12402    source: &str,
12403    ancestry: &ParentIndex<'tree>,
12404) -> Option<CppSentinelDirectBodyClassRegion> {
12405    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
12406        return None;
12407    };
12408    if node.kind() != "function_definition" || !node.has_error() {
12409        return None;
12410    }
12411    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
12412    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
12413    let mut cursor = body.walk();
12414    let candidates = body
12415        .named_children(&mut cursor)
12416        .filter_map(cpp_sentinel_direct_body_class_candidate)
12417        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
12418        .collect::<Vec<_>>();
12419    let [(class_node, template_node)] = candidates.as_slice() else {
12420        return None;
12421    };
12422    let original_body = cpp_body_node(*class_node)?;
12423    let name = class_like_name(*class_node, source, ancestry)?;
12424    if name.is_empty() || cpp_export_macro_token(&name) {
12425        return None;
12426    }
12427
12428    let mut sibling = node.next_named_sibling();
12429    let (class_close_start, class_close_end, class_close_line) = loop {
12430        let current = sibling?;
12431        let next = current.next_named_sibling();
12432        if cpp_is_stray_close_brace(current, source)
12433            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
12434        {
12435            let semicolon = next.expect("checked above");
12436            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
12437                return None;
12438            }
12439            break (
12440                current.start_byte(),
12441                semicolon.end_byte(),
12442                semicolon.end_position().row + 1,
12443            );
12444        }
12445        sibling = next;
12446    };
12447    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
12448    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
12449    let root = tree.root_node();
12450    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
12451    // The region reparse is its own tree, so it needs its own parent index;
12452    // the caller's index answers nothing about these nodes.
12453    let reparsed_ancestry = ParentIndex::new(root);
12454    let reparsed =
12455        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
12456    if reparsed.name != name
12457        || reparsed.declaration_node.start_byte() != class_node.start_byte()
12458        || reparsed.body.start_byte() != original_body.start_byte()
12459        || class_close_start <= reparsed.body.end_byte()
12460        || class_close_end <= class_node.end_byte()
12461    {
12462        return None;
12463    }
12464    Some(CppSentinelDirectBodyClassRegion {
12465        namespace_components,
12466        class_start: reparse_start,
12467        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
12468            node.start_position().row + 1
12469        }),
12470        class_close_end,
12471        class_close_line,
12472        name,
12473    })
12474}
12475
12476/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
12477/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
12478///
12479/// The parser puts the namespace opener and the malformed function in one root
12480/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
12481/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
12482/// the malformed function must begin with an all-caps type, then an ERROR whose
12483/// sole identifier is `namespace`, followed by the inner namespace identifier
12484/// and a compound body; and that body must contain a complete named class or a
12485/// structurally fragmented class prefix. A text reparse cannot prove any of
12486/// those ownership boundaries.
12487fn cpp_nested_namespace_sentinel<'tree>(
12488    node: Node<'tree>,
12489    source: &str,
12490    ancestry: &ParentIndex<'tree>,
12491) -> Option<CppNestedNamespaceSentinel<'tree>> {
12492    if !node.has_error() {
12493        return None;
12494    }
12495
12496    let (function, mut namespace_components) = if node.kind() == "ERROR" {
12497        let mut cursor = node.walk();
12498        let functions = node
12499            .named_children(&mut cursor)
12500            .filter(|child| child.kind() == "function_definition")
12501            .collect::<Vec<_>>();
12502        let [function] = functions.as_slice() else {
12503            return None;
12504        };
12505        if !function.has_error() {
12506            return None;
12507        }
12508        let mut cursor = node.walk();
12509        let children = node.children(&mut cursor).collect::<Vec<_>>();
12510        let function_index = children
12511            .iter()
12512            .position(|child| same_node(*child, *function))?;
12513        let [outer_keyword, outer_name, outer_open] =
12514            children.get(function_index.checked_sub(3)?..function_index)?
12515        else {
12516            return None;
12517        };
12518        if outer_keyword.kind() != "namespace"
12519            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
12520            || outer_open.kind() != "{"
12521        {
12522            return None;
12523        }
12524        (
12525            *function,
12526            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
12527        )
12528    } else if node.kind() == "function_definition" {
12529        let declaration_list = node.parent()?;
12530        let namespace = declaration_list.parent()?;
12531        if declaration_list.kind() != "declaration_list"
12532            || namespace.kind() != "namespace_definition"
12533            || namespace.child_by_field_name("body") != Some(declaration_list)
12534        {
12535            return None;
12536        }
12537        (node, Vec::new())
12538    } else {
12539        return None;
12540    };
12541
12542    let mut cursor = function.walk();
12543    let named = function
12544        .named_children(&mut cursor)
12545        .filter(|child| child.kind() != "comment")
12546        .collect::<Vec<_>>();
12547    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
12548        return None;
12549    };
12550    if first_type.kind() != "type_identifier" {
12551        return None;
12552    }
12553    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
12554    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
12555        return None;
12556    }
12557    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
12558        return None;
12559    }
12560    let inner_keyword = inner_error.named_child(0)?;
12561    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
12562        return None;
12563    }
12564    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
12565        return None;
12566    }
12567    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
12568    if inner_name.is_empty() || body.kind() != "compound_statement" {
12569        return None;
12570    }
12571    namespace_components.push(inner_name);
12572
12573    let mut cursor = body.walk();
12574    let has_complete_class = body.named_children(&mut cursor).any(|child| {
12575        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
12576            cpp_body_node(class_node).is_some()
12577                && class_like_name(class_node, source, ancestry)
12578                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
12579        })
12580    });
12581    if !has_complete_class
12582        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
12583    {
12584        return None;
12585    }
12586
12587    Some(CppNestedNamespaceSentinel {
12588        function,
12589        body: *body,
12590        namespace_components,
12591    })
12592}
12593
12594/// Recognize a namespace-begin sentinel directly beneath the translation unit.
12595///
12596/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
12597/// function whose type is the sentinel, whose declarator is the structured
12598/// qualified name `namespace::a::b`, and whose body contains the namespace
12599/// items. Declaration indexing already reparses this bounded region. The
12600/// inverse scanner retains the original tree, so recover the same namespace
12601/// components from the declarator fields for its lexical-scope metadata.
12602fn cpp_root_namespace_sentinel<'tree>(
12603    node: Node<'tree>,
12604    source: &str,
12605    ancestry: &ParentIndex<'tree>,
12606) -> Option<CppNestedNamespaceSentinel<'tree>> {
12607    if node.kind() != "function_definition"
12608        || !node.has_error()
12609        || node.parent()?.kind() != "translation_unit"
12610    {
12611        return None;
12612    }
12613    let first_type = node.child_by_field_name("type")?;
12614    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
12615    if first_type.kind() != "type_identifier"
12616        || sentinel.is_empty()
12617        || !cpp_export_macro_token(&sentinel)
12618    {
12619        return None;
12620    }
12621    let declarator = node.child_by_field_name("declarator")?;
12622    let body = node.child_by_field_name("body")?;
12623    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
12624        return None;
12625    }
12626    let mut cursor = node.walk();
12627    let named = node
12628        .named_children(&mut cursor)
12629        .filter(|child| child.kind() != "comment")
12630        .collect::<Vec<_>>();
12631    let [named_type, named_declarator, named_body] = named.as_slice() else {
12632        return None;
12633    };
12634    if !same_node(*named_type, first_type)
12635        || !same_node(*named_declarator, declarator)
12636        || !same_node(*named_body, body)
12637    {
12638        return None;
12639    }
12640    let mut declarator_components = Vec::new();
12641    let mut valid_components = true;
12642    walk_named_tree_preorder(declarator, true, |component| {
12643        if !matches!(
12644            component.kind(),
12645            "identifier" | "namespace_identifier" | "type_identifier"
12646        ) {
12647            return WalkControl::Continue;
12648        }
12649        let Some(component) = canonical_cpp_qualified_component(component, source) else {
12650            valid_components = false;
12651            return WalkControl::Break;
12652        };
12653        declarator_components.push(component.name);
12654        WalkControl::SkipChildren
12655    });
12656    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
12657        return None;
12658    }
12659    declarator_components.remove(0);
12660    let namespace_components = declarator_components;
12661    if namespace_components.is_empty()
12662        || namespace_components
12663            .iter()
12664            .any(|component| component.is_empty() || cpp_export_macro_token(component))
12665    {
12666        return None;
12667    }
12668
12669    let mut cursor = body.walk();
12670    let has_complete_class = body.named_children(&mut cursor).any(|child| {
12671        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
12672            cpp_body_node(class_node).is_some()
12673                && class_like_name(class_node, source, ancestry)
12674                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
12675        })
12676    });
12677    if !has_complete_class
12678        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
12679    {
12680        return None;
12681    }
12682
12683    Some(CppNestedNamespaceSentinel {
12684        function: node,
12685        body,
12686        namespace_components,
12687    })
12688}
12689
12690/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
12691/// malformed namespace-sentinel function.  The recovery is deliberately
12692/// structural: the class must be a direct body item, its own class node must be
12693/// erroneous and end before a unique anonymous `}` in the enclosing
12694/// declaration-list, and that namespace's next sibling must be a standalone
12695/// `;`.  The complete interior must pass the existing member-shaped reparse
12696/// gate. This avoids source brace scans and does not borrow a close from an
12697/// unrelated later declaration.
12698fn cpp_sentinel_fragmented_class_tail<'tree>(
12699    function: Node<'tree>,
12700    body: Node<'tree>,
12701    source: &str,
12702    ancestry: &ParentIndex<'tree>,
12703) -> Option<CppSentinelFragmentedClassTail<'tree>> {
12704    let mut cursor = body.walk();
12705    let candidates = body
12706        .named_children(&mut cursor)
12707        .filter_map(|child| {
12708            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
12709                let class_body = cpp_body_node(class_node)?;
12710                if !class_node.has_error() {
12711                    return None;
12712                }
12713                let name = class_like_name(class_node, source, ancestry)?;
12714                let raw_supertypes =
12715                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
12716                        .then(|| extract_cpp_supertypes(class_node, source));
12717                return Some((
12718                    class_node,
12719                    template_node,
12720                    name,
12721                    class_body,
12722                    class_body.start_byte().checked_add(1)?,
12723                    raw_supertypes,
12724                ));
12725            }
12726            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
12727            Some((
12728                child,
12729                None,
12730                prefix.name,
12731                prefix.open,
12732                prefix.open.end_byte(),
12733                prefix.raw_supertypes,
12734            ))
12735        })
12736        .collect::<Vec<_>>();
12737    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
12738        candidates.as_slice()
12739    else {
12740        return None;
12741    };
12742    if name.is_empty() || cpp_export_macro_token(name) {
12743        return None;
12744    }
12745
12746    let (close, semicolon) =
12747        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
12748
12749    let reparse_end = close.start_byte();
12750    if *reparse_start >= reparse_end {
12751        return None;
12752    }
12753    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
12754    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
12755        return None;
12756    }
12757    let class_range = Range {
12758        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
12759        end_byte: semicolon.end_byte(),
12760        start_line: template_node.map_or(class_node.start_position().row, |node| {
12761            node.start_position().row
12762        }) + 1,
12763        end_line: semicolon.end_position().row + 1,
12764    };
12765    Some(CppSentinelFragmentedClassTail {
12766        class_node: *class_node,
12767        template_node: *template_node,
12768        name: name.clone(),
12769        raw_supertypes: raw_supertypes.clone(),
12770        fragmented: FragmentedExportBody {
12771            reparse_start: *reparse_start,
12772            reparse_end,
12773            class_range,
12774        },
12775        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
12776    })
12777}
12778
12779/// Recover the class and out-of-line owner scopes from every malformed
12780/// namespace-sentinel region in `root`.
12781///
12782/// This is the shared structural counterpart to
12783/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
12784/// reuses the visitor's sentinel/class admission predicates instead of parsing
12785/// source text a second time.  The returned values own only ranges and names, so
12786/// they can be retained by an inverted usage scan after the tree borrow ends.
12787pub fn cpp_sentinel_recovered_classes(
12788    root: Node<'_>,
12789    source: &str,
12790) -> Vec<CppSentinelRecoveredClass> {
12791    if !root.has_error() {
12792        return Vec::new();
12793    }
12794    // This scan owns its walk of `root`, so it owns the parent index that walk
12795    // asks its ancestor questions through. Built after the error gate: a clean
12796    // tree returns without paying for one.
12797    let ancestry = ParentIndex::new(root);
12798    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
12799    let mut stack = vec![root];
12800    while let Some(current) = stack.pop() {
12801        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
12802            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
12803        {
12804            let namespace_components = cpp_sentinel_recovered_namespace_components(
12805                recovered.function,
12806                &recovered.namespace_components,
12807                source,
12808            );
12809            let fragmented = cpp_sentinel_fragmented_class_tail(
12810                recovered.function,
12811                recovered.body,
12812                source,
12813                &ancestry,
12814            );
12815            let mut class_candidates = Vec::new();
12816            let mut cursor = recovered.body.walk();
12817            for (class_node, template_node) in recovered
12818                .body
12819                .named_children(&mut cursor)
12820                .filter_map(cpp_sentinel_body_class_candidate)
12821            {
12822                let Some(name) = class_like_name(class_node, source, &ancestry) else {
12823                    continue;
12824                };
12825                if name.is_empty() || cpp_export_macro_token(&name) {
12826                    continue;
12827                }
12828                let is_fragmented = fragmented
12829                    .as_ref()
12830                    .is_some_and(|tail| same_node(tail.class_node, class_node));
12831                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
12832                    continue;
12833                }
12834                let class_range = if is_fragmented {
12835                    fragmented
12836                        .as_ref()
12837                        .map(|tail| tail.fragmented.class_range)
12838                        .expect("fragmented class range is present when class matches")
12839                } else {
12840                    cpp_declaration_range(template_node.unwrap_or(class_node))
12841                };
12842                class_candidates.push((class_range, name));
12843            }
12844            if let Some(fragmented) = fragmented
12845                .as_ref()
12846                .filter(|tail| tail.class_node.kind() == "ERROR")
12847            {
12848                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
12849            }
12850
12851            let mut owner_ranges =
12852                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
12853            cpp_sentinel_extend_unique_owner_ranges(
12854                &mut owner_ranges,
12855                cpp_sentinel_recovered_sibling_owner_ranges(
12856                    recovered.function,
12857                    &namespace_components,
12858                    source,
12859                ),
12860            );
12861            for (class_range, name) in class_candidates {
12862                push_cpp_sentinel_recovered_class(
12863                    &mut recovered_classes,
12864                    cpp_declaration_range(recovered.body),
12865                    &namespace_components,
12866                    class_range,
12867                    name,
12868                    &owner_ranges,
12869                );
12870            }
12871
12872            if let Some(declaration_list) = recovered
12873                .function
12874                .parent()
12875                .filter(|parent| parent.kind() == "declaration_list")
12876            {
12877                let outer_namespace =
12878                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
12879                push_cpp_sentinel_sibling_classes(
12880                    &mut recovered_classes,
12881                    declaration_list,
12882                    recovered.function,
12883                    &outer_namespace,
12884                    source,
12885                    &ancestry,
12886                );
12887            }
12888        } else if let Some(region) =
12889            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
12890        {
12891            let namespace_components = cpp_sentinel_recovered_namespace_components(
12892                current,
12893                &region.namespace_components,
12894                source,
12895            );
12896            let owner_container = current
12897                .parent()
12898                .filter(|parent| parent.kind() == "declaration_list")
12899                .unwrap_or(current);
12900            let owner_ranges =
12901                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
12902            push_cpp_sentinel_recovered_class(
12903                &mut recovered_classes,
12904                cpp_declaration_range(owner_container),
12905                &namespace_components,
12906                Range {
12907                    start_byte: region.class_start,
12908                    end_byte: region.class_close_end,
12909                    start_line: region.class_start_line,
12910                    end_line: region.class_close_line,
12911                },
12912                region.name,
12913                &owner_ranges,
12914            );
12915        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
12916            // A generic sentinel-prefixed class can be reduced as a malformed
12917            // function/ERROR without the explicit `namespace X` token pair.
12918            // Reuse the declaration visitor's bounded reparse and retain only
12919            // the recovered class identity/range here.
12920            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
12921                region;
12922            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
12923                continue;
12924            };
12925            let root = tree.root_node();
12926            let template_node = cpp_sentinel_reparsed_leading_template(root);
12927            // A region reparse is its own tree and needs its own parent index.
12928            let reparsed_ancestry = ParentIndex::new(root);
12929            let Some(reparsed_class) =
12930                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
12931            else {
12932                continue;
12933            };
12934            let class_node = reparsed_class.declaration_node;
12935            let name = reparsed_class.name;
12936            let namespace_components =
12937                cpp_sentinel_recovered_namespace_components(current, &[], source);
12938            let owner_container = current
12939                .parent()
12940                .filter(|parent| parent.kind() == "declaration_list")
12941                .unwrap_or(current);
12942            let mut owner_ranges =
12943                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
12944            cpp_sentinel_extend_unique_owner_ranges(
12945                &mut owner_ranges,
12946                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
12947            );
12948            push_cpp_sentinel_recovered_class(
12949                &mut recovered_classes,
12950                cpp_declaration_range(owner_container),
12951                &namespace_components,
12952                Range {
12953                    start_byte: class_start,
12954                    end_byte: close_end,
12955                    start_line: class_node.start_position().row + 1,
12956                    end_line: class_node.end_position().row + 1,
12957                },
12958                name,
12959                &owner_ranges,
12960            );
12961            if owner_container.kind() == "declaration_list" {
12962                push_cpp_sentinel_sibling_classes(
12963                    &mut recovered_classes,
12964                    owner_container,
12965                    current,
12966                    &namespace_components,
12967                    source,
12968                    &ancestry,
12969                );
12970            }
12971        }
12972
12973        let mut cursor = current.walk();
12974        stack.extend(current.named_children(&mut cursor));
12975    }
12976    // A shallower sentinel can expose nested classes as apparent namespace
12977    // siblings even after a deeper sentinel proves that a containing class
12978    // owns their ranges. Drop those shadow descriptors; scope recovery starts
12979    // from the proven containing class and appends parser-visible class
12980    // ancestors, preserving the full `Outer::Inner` chain.
12981    let shadowed = recovered_classes
12982        .iter()
12983        .map(|candidate| {
12984            recovered_classes.iter().any(|container| {
12985                container.class_range.start_byte <= candidate.class_range.start_byte
12986                    && container.class_range.end_byte >= candidate.class_range.end_byte
12987                    && container.class_range != candidate.class_range
12988                    && container.namespace_scope_components.len()
12989                        > candidate.namespace_scope_components.len()
12990                    && container
12991                        .namespace_scope_components
12992                        .starts_with(&candidate.namespace_scope_components)
12993            })
12994        })
12995        .collect::<Vec<_>>();
12996    let mut index = 0usize;
12997    recovered_classes.retain(|_| {
12998        let keep = !shadowed[index];
12999        index += 1;
13000        keep
13001    });
13002    recovered_classes
13003}
13004
13005/// A flat sentinel can swallow the first class while leaving later classes and
13006/// their out-of-line definitions as ordinary declaration-list siblings.  Once
13007/// the malformed class proves the sentinel envelope, retain those structurally
13008/// complete sibling classes under the same surviving namespace so every member
13009/// owner in the region uses one recovery contract.
13010fn push_cpp_sentinel_sibling_classes<'tree>(
13011    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
13012    declaration_list: Node<'tree>,
13013    sentinel_node: Node<'tree>,
13014    namespace_components: &[String],
13015    source: &str,
13016    ancestry: &ParentIndex<'tree>,
13017) {
13018    let owner_ranges =
13019        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
13020    let namespace_range = cpp_declaration_range(declaration_list);
13021    let mut cursor = declaration_list.walk();
13022    for (class_node, template_node) in declaration_list
13023        .named_children(&mut cursor)
13024        .filter(|child| !same_node(*child, sentinel_node))
13025        .filter_map(cpp_sentinel_body_class_candidate)
13026    {
13027        let Some(name) = class_like_name(class_node, source, ancestry) else {
13028            continue;
13029        };
13030        if name.is_empty()
13031            || cpp_export_macro_token(&name)
13032            || cpp_complete_class_body_close(class_node).is_none()
13033        {
13034            continue;
13035        }
13036        push_cpp_sentinel_recovered_class(
13037            recovered_classes,
13038            namespace_range,
13039            namespace_components,
13040            cpp_declaration_range(template_node.unwrap_or(class_node)),
13041            name,
13042            &owner_ranges,
13043        );
13044    }
13045}
13046
13047fn push_cpp_sentinel_recovered_class(
13048    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
13049    namespace_range: Range,
13050    namespace_components: &[String],
13051    class_range: Range,
13052    name: String,
13053    owner_ranges: &[CppSentinelRecoveredOwner],
13054) {
13055    let mut scope_components = namespace_components.to_vec();
13056    scope_components.push(name);
13057    let owner_ranges = owner_ranges
13058        .iter()
13059        .filter(|owner| owner.scope_components.starts_with(&scope_components))
13060        .cloned()
13061        .collect::<Vec<_>>();
13062    if recovered_classes.iter().any(|existing| {
13063        existing.class_range == class_range && existing.scope_components == scope_components
13064    }) {
13065        return;
13066    }
13067    recovered_classes.push(CppSentinelRecoveredClass {
13068        namespace_range,
13069        namespace_scope_components: namespace_components.to_vec(),
13070        class_range,
13071        scope_components,
13072        owner_ranges,
13073    });
13074}
13075
13076fn cpp_sentinel_recovered_namespace_components(
13077    function: Node<'_>,
13078    recovered_components: &[String],
13079    source: &str,
13080) -> Vec<String> {
13081    let mut ancestor_components = Vec::new();
13082    let mut ancestor = function.parent();
13083    while let Some(current) = ancestor {
13084        if current.kind() == "namespace_definition"
13085            && let Some(name_node) = current.child_by_field_name("name")
13086            && let Some(components) = cpp_name_components(name_node, source)
13087        {
13088            ancestor_components.push(
13089                components
13090                    .into_iter()
13091                    .map(|component| component.name)
13092                    .collect::<Vec<_>>(),
13093            );
13094        }
13095        ancestor = current.parent();
13096    }
13097    ancestor_components.reverse();
13098    let mut ancestors = ancestor_components
13099        .into_iter()
13100        .flatten()
13101        .collect::<Vec<_>>();
13102
13103    let overlap = (0..=ancestors.len().min(recovered_components.len()))
13104        .rev()
13105        .find(|length| {
13106            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
13107        })
13108        .unwrap_or(0);
13109    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
13110    ancestors
13111}
13112
13113fn cpp_sentinel_recovered_owner_ranges(
13114    body: Node<'_>,
13115    namespace_components: &[String],
13116    source: &str,
13117) -> Vec<CppSentinelRecoveredOwner> {
13118    let mut owners = Vec::new();
13119    walk_named_tree_preorder(body, true, |node| {
13120        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
13121    });
13122    owners
13123}
13124
13125fn cpp_sentinel_collect_owner_range(
13126    node: Node<'_>,
13127    namespace_components: &[String],
13128    source: &str,
13129    owners: &mut Vec<CppSentinelRecoveredOwner>,
13130) -> WalkControl {
13131    if node.kind() != "function_definition" {
13132        return WalkControl::Continue;
13133    }
13134    let Some(function_declarator) = extract_function_declarator(node) else {
13135        return WalkControl::Continue;
13136    };
13137    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
13138        return WalkControl::Continue;
13139    };
13140    let Some(mut components) = cpp_name_components(name_node, source) else {
13141        return WalkControl::Continue;
13142    };
13143    if components.len() <= 1 {
13144        return WalkControl::Continue;
13145    }
13146    components.pop();
13147    let mut owner_components = components
13148        .into_iter()
13149        .map(|component| component.name)
13150        .collect::<Vec<_>>();
13151    let overlap = (0..=namespace_components.len().min(owner_components.len()))
13152        .rev()
13153        .find(|length| {
13154            owner_components[..*length]
13155                == namespace_components[namespace_components.len().saturating_sub(*length)..]
13156        })
13157        .unwrap_or(0);
13158    let mut scope_components = namespace_components.to_vec();
13159    scope_components.extend(owner_components.drain(overlap..));
13160    if scope_components.len() <= namespace_components.len() {
13161        return WalkControl::Continue;
13162    }
13163    let range = cpp_declaration_range(node);
13164    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
13165        existing.range == range && existing.scope_components == scope_components
13166    }) {
13167        owners.push(CppSentinelRecoveredOwner {
13168            range,
13169            owner_name_start_byte: name_node.start_byte(),
13170            namespace_component_count: namespace_components.len(),
13171            scope_components,
13172        });
13173    }
13174    WalkControl::Continue
13175}
13176
13177fn cpp_sentinel_extend_unique_owner_ranges(
13178    owners: &mut Vec<CppSentinelRecoveredOwner>,
13179    additional: Vec<CppSentinelRecoveredOwner>,
13180) {
13181    for owner in additional {
13182        if !owners.iter().any(|existing| {
13183            existing.range == owner.range && existing.scope_components == owner.scope_components
13184        }) {
13185            owners.push(owner);
13186        }
13187    }
13188}
13189
13190fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
13191    if node.kind() != "ERROR" || node.named_child_count() != 1 {
13192        return false;
13193    }
13194    let Some(end_name) = node.named_child(0) else {
13195        return false;
13196    };
13197    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
13198        return false;
13199    }
13200    let mut cursor = node.walk();
13201    node.children(&mut cursor)
13202        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
13203}
13204
13205/// Collect owner definitions that the malformed sentinel left as later
13206/// declaration-list siblings. Parser-visible namespace siblings are a hard
13207/// boundary: their declarations must keep their own lexical namespace.
13208fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
13209    parent: Node<'_>,
13210    sentinel_node: Node<'_>,
13211    namespace_components: &[String],
13212    source: &str,
13213) -> Vec<CppSentinelRecoveredOwner> {
13214    let mut owners = Vec::new();
13215    let mut after_sentinel = false;
13216    let mut cursor = parent.walk();
13217    for child in parent.named_children(&mut cursor) {
13218        if !after_sentinel {
13219            if same_node(child, sentinel_node) {
13220                after_sentinel = true;
13221            }
13222            continue;
13223        }
13224        walk_named_tree_preorder(child, true, |node| {
13225            if node.kind() == "namespace_definition" {
13226                return WalkControl::SkipChildren;
13227            }
13228            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
13229        });
13230    }
13231    owners
13232}
13233
13234/// Collect owner definitions after a malformed namespace, stopping only at
13235/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
13236/// enclosing container is not trusted to belong to the recovered namespace.
13237fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
13238    parent: Node<'_>,
13239    sentinel_node: Node<'_>,
13240    namespace_components: &[String],
13241    source: &str,
13242) -> Option<Vec<CppSentinelRecoveredOwner>> {
13243    let mut owners = Vec::new();
13244    let mut after_namespace = false;
13245    let mut cursor = parent.walk();
13246    for child in parent.named_children(&mut cursor) {
13247        if !after_namespace {
13248            if same_node(child, sentinel_node) {
13249                after_namespace = true;
13250            }
13251            continue;
13252        }
13253        if cpp_sentinel_namespace_end(child, source) {
13254            return Some(owners);
13255        }
13256        walk_named_tree_preorder(child, true, |node| {
13257            if node.kind() == "namespace_definition" {
13258                return WalkControl::SkipChildren;
13259            }
13260            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
13261        });
13262    }
13263    None
13264}
13265
13266fn cpp_sentinel_recovered_sibling_owner_ranges(
13267    sentinel_node: Node<'_>,
13268    namespace_components: &[String],
13269    source: &str,
13270) -> Vec<CppSentinelRecoveredOwner> {
13271    let Some(declaration_list) = sentinel_node
13272        .parent()
13273        .filter(|parent| parent.kind() == "declaration_list")
13274    else {
13275        return Vec::new();
13276    };
13277    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
13278        declaration_list,
13279        sentinel_node,
13280        namespace_components,
13281        source,
13282    );
13283
13284    let Some(namespace) = declaration_list
13285        .parent()
13286        .filter(|parent| parent.kind() == "namespace_definition")
13287    else {
13288        return owners;
13289    };
13290    let Some(outer_parent) = namespace.parent() else {
13291        return owners;
13292    };
13293    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
13294        outer_parent,
13295        namespace,
13296        namespace_components,
13297        source,
13298    ) {
13299        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
13300    }
13301    owners
13302}
13303
13304fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
13305    let mut current = function_declarator.child_by_field_name("declarator")?;
13306    loop {
13307        if let Some(name) = macro_decorated_unqualified_name(current) {
13308            current = name;
13309            continue;
13310        }
13311        if matches!(
13312            current.kind(),
13313            "qualified_identifier"
13314                | "scoped_identifier"
13315                | "scoped_type_identifier"
13316                | "identifier"
13317                | "field_identifier"
13318                | "operator_name"
13319                | "destructor_name"
13320                | "literal_operator_name"
13321        ) {
13322            return Some(current);
13323        }
13324        current = current
13325            .child_by_field_name("declarator")
13326            .or_else(|| current.child_by_field_name("name"))
13327            .or_else(|| last_named_child(current))?;
13328    }
13329}
13330
13331/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
13332/// `::` between the halves. The scope is an attribute-like macro token, not
13333/// a namespace; `Name` is the declared name.
13334///
13335/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
13336/// tree-sitter joins the attribute macro and the constructor name into one
13337/// `qualified_identifier` and marks the separator it had to invent as MISSING.
13338/// That flag is the grammar's own record that no separator is in the source,
13339/// so read it instead of looking for `::` in the node text. A genuine
13340/// `Outer::Inner` keeps a present separator and is declined here, and so is
13341/// the explicit-global `::name`, whose `::` is present and whose scope is
13342/// absent.
13343fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
13344    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
13345        return None;
13346    }
13347    let mut cursor = node.walk();
13348    if node
13349        .children(&mut cursor)
13350        .any(|child| child.kind() == "::" && !child.is_missing())
13351    {
13352        return None;
13353    }
13354    node.child_by_field_name("name")
13355}
13356
13357fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
13358    if let Some(name) = macro_decorated_unqualified_name(node) {
13359        return cpp_name_components(name, source);
13360    }
13361    match node.kind() {
13362        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13363            let mut components = match node.child_by_field_name("scope") {
13364                Some(scope) => cpp_name_components(scope, source)?,
13365                None => Vec::new(),
13366            };
13367            let name = node.child_by_field_name("name")?;
13368            components.push(canonical_cpp_qualified_component(name, source)?);
13369            Some(components)
13370        }
13371        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
13372    }
13373}
13374
13375fn cpp_sentinel_fragment_boundary<'tree>(
13376    function: Node<'tree>,
13377    class_node: Node<'tree>,
13378    class_body: Node<'tree>,
13379    source: &str,
13380) -> Option<(Node<'tree>, Node<'tree>)> {
13381    let declaration_list = function.parent()?;
13382    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
13383        return None;
13384    }
13385    let namespace = declaration_list.parent()?;
13386    if namespace.kind() != "namespace_definition"
13387        || namespace.child_by_field_name("body") != Some(declaration_list)
13388    {
13389        return None;
13390    }
13391    let mut cursor = declaration_list.walk();
13392    let closes = declaration_list
13393        .children(&mut cursor)
13394        .filter(|child| {
13395            !child.is_named()
13396                && child.kind() == "}"
13397                && child.start_byte() >= function.end_byte()
13398                && child.start_byte() > class_node.end_byte()
13399                && child.start_byte() > class_body.start_byte()
13400        })
13401        .collect::<Vec<_>>();
13402    let [close] = closes.as_slice() else {
13403        return None;
13404    };
13405    let semicolon = namespace.next_named_sibling()?;
13406    if !cpp_is_stray_semicolon(semicolon, source)
13407        || close.end_byte() != namespace.end_byte()
13408        || semicolon.start_byte() < namespace.end_byte()
13409    {
13410        return None;
13411    }
13412    Some((*close, semicolon))
13413}
13414
13415/// Detect the bogus declaration/function tree that tree-sitter recovers for a
13416/// region prefixed by an object-like macro sentinel the parser cannot see
13417/// (issue #941), and return the byte range `[start, end)` of the swallowed
13418/// declaration interior to reparse.
13419///
13420/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
13421/// `function_definition` whose first non-comment named child is the sentinel
13422/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
13423/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
13424/// the real items.
13425/// `start` is the end of the sentinel identifier -- everything after it is the
13426/// genuine source. `end` is the node's end, extended across any trailing empty
13427/// `;` statement the mis-parse displaced past the node (the class/struct closing
13428/// semicolon), so the reparse sees a complete, brace-balanced item.
13429///
13430/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
13431/// node (`has_error`). Unknown annotation/export macros can make a real callable
13432/// error-recovered even though tree-sitter still preserves its declarator, so a
13433/// preserved callable is admitted only when a displaced class keyword precedes
13434/// that declarator. The clean-reparse-to-items gate in
13435/// `cpp_reparsed_items_are_indexable` is the final arbiter.
13436/// Return the reparse start and, when present, the structurally recovered class
13437/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
13438/// separately from the reparse start because an opaque template-declaration
13439/// macro may precede it.
13440fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
13441    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
13442    {
13443        return None;
13444    }
13445    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
13446    // retain a valid function declarator despite the unknown export macro making
13447    // the outer node erroneous. Remember that declarator for the ordering gate
13448    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
13449    // class keyword precedes a spurious callable assembled from a later member.
13450    let mut declarator_cursor = node.walk();
13451    let preserved_callable = node
13452        .children_by_field_name("declarator", &mut declarator_cursor)
13453        .find_map(extract_function_declarator);
13454    // Leading documentation comments are attached to the malformed
13455    // `function_definition` as named children.  They are not part of the
13456    // sentinel prefix, so select the first non-comment child structurally
13457    // rather than requiring the sentinel to be child zero.  This is the shape
13458    // emitted for nlohmann/json's `basic_json`: its class documentation comment
13459    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
13460    // envelope otherwise ends at the first nested union.
13461    let mut cursor = node.walk();
13462    let first = node
13463        .named_children(&mut cursor)
13464        .find(|child| child.kind() != "comment")?;
13465    if first.kind() != "type_identifier" {
13466        return None;
13467    }
13468    let sentinel = normalize_cpp_whitespace(node_text(first, source));
13469    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13470        return None;
13471    }
13472    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
13473    // makes the trailing sentinel of one region and the leading sentinel of the
13474    // next both land as bare macro-token identifiers ahead of the real content.
13475    // Advance past every leading macro-token identifier so the reparse begins at
13476    // genuine source rather than another sentinel that would re-form the bogus
13477    // shape and fail the reparse gate.
13478    let mut start = first.end_byte();
13479    let mut after_first = false;
13480    let mut cursor = node.walk();
13481    for child in node.named_children(&mut cursor) {
13482        if !after_first {
13483            if same_node(child, first) {
13484                after_first = true;
13485            }
13486            continue;
13487        }
13488        if matches!(child.kind(), "identifier" | "type_identifier")
13489            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
13490        {
13491            start = child.end_byte();
13492        } else {
13493            break;
13494        }
13495    }
13496    // An additional opaque template-declaration macro before a class can be
13497    // folded into the bogus function's qualified declarator.  In that shape
13498    // the macro is not a direct sibling we can skip above; tree-sitter exposes
13499    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
13500    // Reparse from that keyword (or a real preceding `template` keyword) so the
13501    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
13502    // a class nested in a genuine sentinel-wrapped namespace lies after the
13503    // body opening and must not change the established region start.
13504    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
13505    let mut class_start = None;
13506    let mut template_start = None;
13507    let mut stack = vec![node];
13508    while let Some(current) = stack.pop() {
13509        if current.start_byte() >= prefix_end {
13510            continue;
13511        }
13512        if matches!(
13513            current.kind(),
13514            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
13515        ) {
13516            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
13517                "class" | "struct" | "union" | "enum" => {
13518                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
13519                        seen.min(current.start_byte())
13520                    }));
13521                }
13522                "template" => {
13523                    template_start =
13524                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
13525                            seen.min(current.start_byte())
13526                        }));
13527                }
13528                _ => {}
13529            }
13530        }
13531        let mut cursor = current.walk();
13532        stack.extend(current.children(&mut cursor));
13533    }
13534    if preserved_callable.is_some_and(|callable| {
13535        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
13536    }) {
13537        return None;
13538    }
13539    if let Some(class_start) = class_start {
13540        start = template_start
13541            .filter(|template_start| *template_start < class_start)
13542            .unwrap_or(class_start);
13543    }
13544    Some((start, class_start))
13545}
13546
13547/// Locate a sentinel-prefixed class whose malformed declaration was split across
13548/// root-level siblings. The true class close is represented structurally as a
13549/// lone `}` error followed by the class's displaced `;`; nested method/body
13550/// errors are not direct siblings of the sentinel node and therefore cannot
13551/// satisfy this pair.
13552fn cpp_sentinel_macro_class_region<'tree>(
13553    node: Node<'tree>,
13554    source: &str,
13555) -> Option<(usize, usize, usize, usize, usize, usize)> {
13556    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
13557        return None;
13558    };
13559    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
13560        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
13561        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
13562    if class_start >= body_open_start {
13563        return None;
13564    }
13565    let sibling_close = {
13566        let mut sibling = node.next_named_sibling();
13567        let mut found = None;
13568        while let Some(current) = sibling {
13569            let next = current.next_named_sibling();
13570            if cpp_is_stray_close_brace(current, source)
13571                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13572            {
13573                let semicolon = next.expect("checked above");
13574                found = Some((
13575                    current.start_byte(),
13576                    semicolon.end_byte(),
13577                    semicolon.end_position().row + 1,
13578                ));
13579                break;
13580            }
13581            sibling = next;
13582        }
13583        found
13584    };
13585    // A stray `};` sibling is this class's close only when the bounded reparse
13586    // agrees the first body-bearing class ENDS there. When the malformed
13587    // envelope swallowed the class's true close, the scan can promote a much
13588    // later scope's close instead -- in protobuf-generated headers
13589    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
13590    // `struct TableStruct_*` paired with the first message class's `};`, making
13591    // the recovered "class body" span whole `namespace {}` blocks and minting
13592    // namespace-scope classes as nested members of the recovered class, which
13593    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
13594    // (#2275). On disagreement, fall through to the suffix-reparse boundary
13595    // below, which derives the close from the class node's own balanced body
13596    // range.
13597    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
13598        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
13599            return false;
13600        };
13601        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
13602        // A region reparse is its own tree and needs its own parent index.
13603        let reparsed_ancestry = ParentIndex::new(tree.root_node());
13604        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
13605            tree.root_node(),
13606            template_node,
13607            source,
13608            &reparsed_ancestry,
13609        ) else {
13610            return false;
13611        };
13612        let body = reparsed_class.body;
13613        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
13614    });
13615    let (class_close_start, class_close_end, class_close_line) =
13616        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
13617            (class_close_start, class_close_end, class_close_line)
13618        } else {
13619            // When the malformed envelope itself is an ERROR, tree-sitter can
13620            // leave the class's balanced close in the source while promoting
13621            // all following members to siblings. Reparse the complete suffix
13622            // and use the first body-bearing class node's own field range as
13623            // the partition boundary. This keeps balancing in tree-sitter and
13624            // preserves the source's original byte offsets.
13625            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
13626            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
13627            // A region reparse is its own tree and needs its own parent index.
13628            let reparsed_ancestry = ParentIndex::new(tree.root_node());
13629            let reparsed_class = cpp_sentinel_reparsed_class(
13630                tree.root_node(),
13631                template_node,
13632                source,
13633                &reparsed_ancestry,
13634            )?;
13635            let body = reparsed_class.body;
13636            let class_close_end = body.end_byte();
13637            let class_close_start = class_close_end.checked_sub(1)?;
13638            let class_close_line = body.end_position().row + 1;
13639            (class_close_start, class_close_end, class_close_line)
13640        };
13641    if class_close_start <= class_start {
13642        return None;
13643    }
13644
13645    // Reparse only far enough to expose the class body opening. This is a
13646    // structured check that the candidate really begins with a body-bearing
13647    // class-like item; the original malformed tree cannot provide that node.
13648    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13649    let class_root = tree.root_node();
13650    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
13651    // A region reparse is its own tree and needs its own parent index.
13652    let reparsed_ancestry = ParentIndex::new(class_root);
13653    let reparsed_class =
13654        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
13655    let body = reparsed_class.body;
13656    // The class body opening must agree with the malformed wrapper's structured
13657    // body field. This rejects an inner nested class while permitting later
13658    // members to remain fragmented as root-level siblings in the bounded parse.
13659    if body.start_byte() != body_open_start {
13660        return None;
13661    }
13662    let body_start = body.start_byte().checked_add(1)?;
13663    (body_start < class_close_start).then_some((
13664        reparse_start,
13665        class_start,
13666        body_start,
13667        class_close_start,
13668        class_close_end,
13669        class_close_line,
13670    ))
13671}
13672
13673/// Find the `{` token immediately following the class/struct/union/enum token
13674/// at `class_start` in the malformed tree. The token is anonymous in the C++
13675/// grammar, so this deliberately walks all children (not only named children)
13676/// and relies on sibling structure rather than source-text searching.
13677fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
13678    let mut stack = vec![node];
13679    while let Some(current) = stack.pop() {
13680        if current.start_byte() == class_start
13681            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
13682        {
13683            let mut sibling = current.next_sibling();
13684            while let Some(candidate) = sibling {
13685                if candidate.kind() == "{" {
13686                    return Some(candidate.start_byte());
13687                }
13688                sibling = candidate.next_sibling();
13689            }
13690        }
13691        let mut cursor = current.walk();
13692        stack.extend(current.children(&mut cursor));
13693    }
13694    None
13695}
13696
13697/// The class body that tree-sitter displaced out of a sentinel-prefixed
13698/// declaration and left as the malformed node's next sibling.
13699///
13700/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
13701/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
13702/// the last child of that `ERROR` and its `{` opens a sibling
13703/// `compound_statement` instead. The body is still the malformed tree's own
13704/// structured token, which is what the caller's `body.start_byte() !=
13705/// body_open_start` agreement check needs; it just is not reachable by walking
13706/// forward from the class token inside the node.
13707fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
13708    node.next_named_sibling()
13709        .filter(|sibling| sibling.kind() == "compound_statement")
13710}
13711
13712fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
13713    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
13714    let mut end = if class_start.is_some() {
13715        cpp_macro_prefixed_class_end(source, start)?
13716    } else {
13717        node.end_byte()
13718    };
13719    if class_start.is_none()
13720        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
13721    {
13722        end = end.max(namespace_end);
13723    }
13724    let mut sibling = node.next_named_sibling();
13725    while let Some(current) = sibling {
13726        if !cpp_is_stray_semicolon(current, source) {
13727            break;
13728        }
13729        end = current.end_byte();
13730        sibling = current.next_named_sibling();
13731    }
13732    (start < end).then_some((start, end))
13733}
13734
13735/// Extend a sentinel reparse through a following namespace that tree-sitter
13736/// flattened into the sentinel node's sibling list.
13737///
13738/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
13739/// unknown macro becomes a false function return type and consumes the first
13740/// namespace body. A second `namespace detail` then loses its enclosing node:
13741/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
13742/// attaches its declarations to the surrounding error tree. Reparse from that
13743/// structured keyword so tree-sitter, rather than a source-text brace scan,
13744/// supplies the complete namespace boundary.
13745fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
13746    let mut sibling = node.next_sibling();
13747    let keyword = loop {
13748        let candidate = sibling?;
13749        sibling = candidate.next_sibling();
13750        if candidate.kind() != "comment" {
13751            break candidate;
13752        }
13753    };
13754    if keyword.kind() != "namespace" {
13755        return None;
13756    }
13757    let name = loop {
13758        let candidate = sibling?;
13759        sibling = candidate.next_sibling();
13760        if candidate.kind() != "comment" {
13761            break candidate;
13762        }
13763    };
13764    if cpp_namespace_name_components(name, source).is_empty() {
13765        return None;
13766    }
13767    let open = loop {
13768        let candidate = sibling?;
13769        sibling = candidate.next_sibling();
13770        if candidate.kind() != "comment" {
13771            break candidate;
13772        }
13773    };
13774    if open.kind() != "{" {
13775        return None;
13776    }
13777
13778    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
13779    let root = tree.root_node();
13780    let mut cursor = root.walk();
13781    let namespace = root
13782        .named_children(&mut cursor)
13783        .find(|candidate| candidate.kind() != "comment")?;
13784    (namespace.kind() == "namespace_definition"
13785        && namespace.start_byte() == keyword.start_byte()
13786        && namespace.child_by_field_name("body").is_some())
13787    .then_some(namespace.end_byte())
13788}
13789
13790/// Parse the source suffix beginning at a structurally recovered class/template
13791/// keyword and return the end of its first body-bearing class item.  The parser,
13792/// rather than a brace scanner, owns nested-body balancing.  This is needed when
13793/// the original error tree truncates the class and scatters later members as
13794/// top-level siblings.
13795fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
13796    let tree = cpp_reparse_region_items(source, start, source.len())?;
13797    let root = tree.root_node();
13798    let mut cursor = root.walk();
13799    for item in root.named_children(&mut cursor) {
13800        if item.end_byte() <= start || item.kind() == "comment" {
13801            continue;
13802        }
13803        let mut stack = vec![item];
13804        while let Some(current) = stack.pop() {
13805            if matches!(
13806                current.kind(),
13807                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
13808            ) && cpp_body_node(current).is_some()
13809            {
13810                return Some(current.end_byte());
13811            }
13812            let mut cursor = current.walk();
13813            stack.extend(current.named_children(&mut cursor));
13814        }
13815        // The recovered prefix is required to begin with the class item.  If
13816        // the first real item is something else, fail closed rather than skip
13817        // arbitrary source looking for a later class.
13818        return None;
13819    }
13820    None
13821}
13822
13823/// An empty `;` statement: the displaced closing semicolon of a struct/class that
13824/// the sentinel mis-parse split off past the bogus function node.
13825fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
13826    node.kind() == "expression_statement"
13827        && node.named_child_count() == 0
13828        && node_text(node, source).trim() == ";"
13829}
13830
13831/// Recover the member that follows CPython's object header macro when
13832/// tree-sitter folds both declarations into one malformed field.
13833///
13834/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
13835/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
13836/// sole child is the real field identifier. The shape is intentionally exact:
13837/// a pointer declarator and similarly malformed fields do not provide the
13838/// same evidence that this is the CPython macro boundary.
13839#[derive(Clone, Copy)]
13840pub(crate) struct RecoveredPyObjectHeadField<'tree> {
13841    pub(crate) type_node: Node<'tree>,
13842    pub(crate) declarator: Node<'tree>,
13843}
13844
13845pub(crate) fn recovered_pyobject_head_field<'tree>(
13846    node: Node<'tree>,
13847    source: &str,
13848) -> Option<RecoveredPyObjectHeadField<'tree>> {
13849    if node.kind() != "field_declaration" {
13850        return None;
13851    }
13852    let type_node = node.child_by_field_name("type")?;
13853    if type_node.kind() != "type_identifier"
13854        || node_text(type_node, source).trim() != "PyObject_HEAD"
13855    {
13856        return None;
13857    }
13858    let pseudo_declarator = node.child_by_field_name("declarator")?;
13859    if pseudo_declarator.kind() != "field_identifier" {
13860        return None;
13861    }
13862    let mut cursor = node.walk();
13863    let errors = node
13864        .named_children(&mut cursor)
13865        .filter(|child| child.kind() == "ERROR")
13866        .collect::<Vec<_>>();
13867    let [error] = errors.as_slice() else {
13868        return None;
13869    };
13870    if error.start_byte() < pseudo_declarator.end_byte() || error.named_child_count() != 1 {
13871        return None;
13872    }
13873    let declarator = error.named_child(0)?;
13874    (declarator.kind() == "identifier").then_some(RecoveredPyObjectHeadField {
13875        type_node: pseudo_declarator,
13876        declarator,
13877    })
13878}
13879
13880/// Recover the real field name when a leading object-like annotation macro
13881/// displaces a qualified type into tree-sitter's bit-field recovery shape.
13882///
13883/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
13884/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
13885/// `bitfield_clause` containing an error plus an assignment.  The assignment's
13886/// left field is the only structured declaration name in that malformed tail.
13887/// A real bit-field is excluded by the all-caps macro type and required error.
13888fn recovered_macro_qualified_field_declarators<'tree>(
13889    node: Node<'tree>,
13890    source: &str,
13891) -> Option<Vec<Node<'tree>>> {
13892    if node.kind() != "field_declaration" {
13893        return None;
13894    }
13895    let macro_type = node.child_by_field_name("type")?;
13896    if macro_type.kind() != "type_identifier"
13897        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
13898    {
13899        return None;
13900    }
13901    let pseudo_declarator = node.child_by_field_name("declarator")?;
13902    if pseudo_declarator.kind() != "field_identifier" {
13903        return None;
13904    }
13905    let mut cursor = node.walk();
13906    let clause = node
13907        .named_children(&mut cursor)
13908        .find(|child| child.kind() == "bitfield_clause")?;
13909    if !(0..clause.named_child_count()).any(|index| {
13910        clause
13911            .named_child(index)
13912            .is_some_and(|child| child.kind() == "ERROR")
13913    }) {
13914        return None;
13915    }
13916    let mut recovered = Vec::new();
13917    let mut stack = vec![clause];
13918    while let Some(current) = stack.pop() {
13919        if current.kind() == "assignment_expression"
13920            && let Some(left) = current.child_by_field_name("left")
13921            && extract_variable_name(left, source).is_some()
13922        {
13923            recovered.push(left);
13924            break;
13925        }
13926        let mut cursor = current.walk();
13927        stack.extend(current.named_children(&mut cursor));
13928    }
13929    if recovered.is_empty() {
13930        return None;
13931    }
13932    let mut cursor = node.walk();
13933    recovered.extend(
13934        node.children_by_field_name("declarator", &mut cursor)
13935            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
13936    );
13937    Some(recovered)
13938}
13939
13940/// Recover a macro-qualified constructor that tree-sitter represents as one
13941/// field declaration. The constructor call remains inside the direct recovery
13942/// error, while each member initializer becomes a false function declarator.
13943/// The class owner proves the constructor name and lets the caller ignore those
13944/// initializer declarators.
13945fn recovered_macro_qualified_constructor_call<'tree>(
13946    node: Node<'tree>,
13947    class_name: &str,
13948    source: &str,
13949) -> Option<Node<'tree>> {
13950    if node.kind() != "field_declaration" {
13951        return None;
13952    }
13953    let macro_type = node.child_by_field_name("type")?;
13954    if macro_type.kind() != "type_identifier"
13955        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
13956    {
13957        return None;
13958    }
13959    let mut cursor = node.walk();
13960    let bitfield = node
13961        .named_children(&mut cursor)
13962        .find(|child| child.kind() == "bitfield_clause")?;
13963    let error = bitfield
13964        .named_child(0)
13965        .filter(|child| child.kind() == "ERROR")?;
13966    let mut stack = vec![error];
13967    while let Some(current) = stack.pop() {
13968        if current.kind() == "call_expression"
13969            && current
13970                .child_by_field_name("function")
13971                .is_some_and(|function| node_text(function, source) == class_name)
13972            && current
13973                .child_by_field_name("arguments")
13974                .is_some_and(|arguments| arguments.kind() == "argument_list")
13975        {
13976            return Some(current);
13977        }
13978        let mut cursor = current.walk();
13979        stack.extend(current.named_children(&mut cursor));
13980    }
13981    None
13982}
13983
13984/// Recover a macro-qualified member function declaration that tree-sitter
13985/// represents as a pseudo-field. An object-like export macro before a qualified
13986/// return type can displace the namespace and type into an ERROR/bitfield
13987/// recovery, leaving the callable as a structured `call_expression`.
13988///
13989/// The caller must route this shape before ordinary declarator classification;
13990/// otherwise the displaced namespace identifier is published as a field.
13991fn recovered_macro_qualified_function_call<'tree>(
13992    node: Node<'tree>,
13993    source: &str,
13994) -> Option<Node<'tree>> {
13995    if node.kind() != "field_declaration" {
13996        return None;
13997    }
13998    let macro_type = node.child_by_field_name("type")?;
13999    if macro_type.kind() != "type_identifier"
14000        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14001    {
14002        return None;
14003    }
14004    let declarator = node.child_by_field_name("declarator")?;
14005    if declarator.kind() != "field_identifier" {
14006        return None;
14007    }
14008    let mut cursor = node.walk();
14009    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
14010    if !named.iter().any(|child| {
14011        child.kind() == "storage_class_specifier"
14012            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
14013    }) {
14014        return None;
14015    }
14016    let bitfield = named
14017        .iter()
14018        .find(|child| child.kind() == "bitfield_clause")?;
14019    let mut bitfield_cursor = bitfield.walk();
14020    let payload = bitfield
14021        .named_children(&mut bitfield_cursor)
14022        .collect::<Vec<_>>();
14023    let [displaced_error, call] = payload.as_slice() else {
14024        return None;
14025    };
14026    if displaced_error.kind() != "ERROR"
14027        || displaced_error.named_child_count() != 1
14028        || displaced_error
14029            .named_child(0)
14030            .is_none_or(|child| child.kind() != "identifier")
14031        || call.kind() != "call_expression"
14032        || call
14033            .child_by_field_name("function")
14034            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
14035        || call
14036            .child_by_field_name("arguments")
14037            .is_none_or(|arguments| arguments.kind() != "argument_list")
14038    {
14039        return None;
14040    }
14041    Some(*call)
14042}
14043
14044fn recovered_macro_qualified_function_parameters(
14045    arguments: Node<'_>,
14046    source: &str,
14047) -> Option<(String, Vec<String>)> {
14048    if arguments.kind() != "argument_list" {
14049        return None;
14050    }
14051    let mut cursor = arguments.walk();
14052    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
14053    if named.is_empty() {
14054        return Some(("()".to_string(), Vec::new()));
14055    }
14056    let mut types = Vec::new();
14057    let mut labels = Vec::new();
14058    let mut index = 0;
14059    while index < named.len() {
14060        let parameter_type = named[index];
14061        let parameter_name = named.get(index + 1).copied()?;
14062        if !matches!(
14063            parameter_type.kind(),
14064            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
14065        ) || parameter_name.kind() != "ERROR"
14066            || parameter_name.named_child_count() != 1
14067            || parameter_name
14068                .named_child(0)
14069                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
14070        {
14071            return None;
14072        }
14073        let parameter_name = parameter_name.named_child(0)?;
14074        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
14075        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
14076        index += 2;
14077    }
14078    Some((format!("({})", types.join(", ")), labels))
14079}
14080
14081/// Recognize the phantom field tree-sitter emits for a macro-qualified
14082/// function return type.  For example,
14083/// `static API result_type ThresholdForSmallA() { ... }` can become a
14084/// `field_declaration` (`API` as the type and `result_type` as a field name)
14085/// followed by a clean `function_definition` for `ThresholdForSmallA`.
14086///
14087/// Keep this predicate entirely tied to the CST envelope: the type must be an
14088/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
14089/// the declaration must carry a missing semicolon rather than a real one, and
14090/// the immediate named sibling must expose a function declarator.  A real
14091/// macro-decorated field with an explicit semicolon therefore remains a field.
14092pub fn recovered_macro_return_type_node<'tree>(
14093    node: Node<'tree>,
14094    source: &str,
14095) -> Option<Node<'tree>> {
14096    if node.kind() != "field_declaration" {
14097        return None;
14098    }
14099    let macro_type = node.child_by_field_name("type")?;
14100    if macro_type.kind() != "type_identifier"
14101        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14102    {
14103        return None;
14104    }
14105    let declarator = node.child_by_field_name("declarator")?;
14106    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
14107        return None;
14108    }
14109    let mut has_missing_semicolon = false;
14110    let mut has_real_semicolon = false;
14111    for index in 0..node.child_count() {
14112        let Some(child) = node.child(index) else {
14113            continue;
14114        };
14115        if child.kind() != ";" {
14116            continue;
14117        }
14118        if child.is_missing() {
14119            has_missing_semicolon = true;
14120        } else {
14121            has_real_semicolon = true;
14122        }
14123    }
14124    if !has_missing_semicolon || has_real_semicolon {
14125        return None;
14126    }
14127    let mut next = node.next_named_sibling();
14128    while next.is_some_and(|sibling| sibling.kind() == "comment") {
14129        next = next.and_then(|sibling| sibling.next_named_sibling());
14130    }
14131    let next = next?;
14132    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
14133        return None;
14134    }
14135    let function_declarator = next.child_by_field_name("declarator")?;
14136    extract_function_declarator(function_declarator).map(|_| declarator)
14137}
14138
14139/// Whether `name` is a type parameter of a template declaration that lexically
14140/// encloses `node`. The malformed macro-return field uses the parameter name as
14141/// its pseudo-declarator; preserving that field is necessary to publish a
14142/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
14143/// ancestors instead of interpreting source text so nested templates and
14144/// parser-recovered regions retain their real lexical scopes.
14145pub(crate) fn cpp_active_template_type_parameter<'tree>(
14146    node: Node<'tree>,
14147    name: &str,
14148    source: &str,
14149    ancestry: &ParentIndex<'tree>,
14150) -> bool {
14151    let mut ancestor = ancestry.parent(node);
14152    while let Some(current) = ancestor {
14153        if current.kind() == "template_declaration"
14154            && let Some(parameters) = current.child_by_field_name("parameters")
14155        {
14156            let mut cursor = parameters.walk();
14157            if parameters.named_children(&mut cursor).any(|parameter| {
14158                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14159                    && cpp_template_parameter_name(parameter, source)
14160                        .is_some_and(|parameter_name| parameter_name == name)
14161            }) {
14162                return true;
14163            }
14164        }
14165        ancestor = ancestry.parent(current);
14166    }
14167    false
14168}
14169
14170/// Reparse the region `[start, end)` of `source` as C++, confined to the region
14171/// via included ranges so every reparsed node keeps its original byte offset and
14172/// line number. The existing visitors read node text from the original source,
14173/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
14174/// `parse_rust_region_tree` technique.
14175fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
14176    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
14177}
14178
14179fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
14180    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
14181        return None;
14182    }
14183    let semicolon = node.next_sibling()?;
14184    if semicolon.kind() != ";" || semicolon.is_missing() {
14185        return None;
14186    }
14187    let row = node.start_position().row;
14188    let mut start = node.start_byte();
14189    let mut sibling = node.prev_sibling();
14190    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
14191        if previous.kind() == ";" {
14192            break;
14193        }
14194        start = previous.start_byte();
14195        sibling = previous.prev_sibling();
14196    }
14197    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
14198}
14199
14200/// One `RET name _(( args ));`-style K&R prototype-macro invocation
14201/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
14202/// byte spans a clean reparse needs, in original-source order.
14203struct PrototypeMacroCandidate {
14204    /// Where the return type and declared name begin.
14205    run_start: usize,
14206    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
14207    /// exclusive end of the first reparse span.
14208    identifier_start: usize,
14209    /// The inner `(`'s start byte: the start of the second reparse span.
14210    /// This paren is kept so the reparse sees exactly one parameter-list
14211    /// paren pair.
14212    inner_open_start: usize,
14213    /// Byte just past the inner `)`: the end of the second reparse span.
14214    inner_close_end: usize,
14215    /// Byte just past the outer `)`: the start of the third reparse span.
14216    outer_close_end: usize,
14217    /// Byte just past the terminating `;`: the end of the third reparse
14218    /// span, and of the whole candidate.
14219    semicolon_end: usize,
14220}
14221
14222impl PrototypeMacroCandidate {
14223    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
14224    /// one included-range tree: the return type and name, the parenthesized
14225    /// argument list, and the terminating `;`. The macro token and its outer
14226    /// wrapping parenthesis are omitted -- deleting them is exactly what the
14227    /// (absent) preprocessor macro expansion would do.
14228    fn ranges(&self) -> [(usize, usize); 3] {
14229        [
14230            (self.run_start, self.identifier_start),
14231            (self.inner_open_start, self.inner_close_end),
14232            (self.outer_close_end, self.semicolon_end),
14233        ]
14234    }
14235}
14236
14237/// A live terminating semicolon owned directly by `node`.
14238fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
14239    node.child(node.child_count().checked_sub(1)?)
14240        .filter(|child| child.kind() == ";" && !child.is_missing())
14241}
14242
14243/// The known pre-ANSI prototype macros whose expansion is exactly their one
14244/// argument. The malformed CST cannot prove that an arbitrary identifier has
14245/// that definition, so unknown spellings fail closed rather than turning an
14246/// ordinary broken declaration into a Function (issue #2932).
14247fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
14248    matches!(
14249        node.kind(),
14250        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
14251    ) && matches!(
14252        normalize_cpp_whitespace(node_text(node, source)).as_str(),
14253        "_" | "__P" | "OF" | "PROTO"
14254    )
14255}
14256
14257/// Read tree-sitter's recovery for `declared_name MACRO`: a
14258/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
14259/// declared name, and whose `name` is a known prototype macro.
14260fn cpp_prototype_macro_qualified_parts<'tree>(
14261    node: Node<'tree>,
14262    source: &str,
14263) -> Option<(Node<'tree>, Node<'tree>)> {
14264    if node.kind() != "qualified_identifier" {
14265        return None;
14266    }
14267    let declared_name = node
14268        .child_by_field_name("scope")
14269        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
14270    let macro_name = macro_decorated_unqualified_name(node)?;
14271    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
14272}
14273
14274/// The inner parenthesized node of tree-sitter's structured
14275/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
14276/// the macro invocation's outer parentheses while retaining the one pair an
14277/// ordinary function declarator needs. No delimiter search is involved: both
14278/// pairs and their ownership come from the CST.
14279fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
14280    if arguments.kind() != "argument_list"
14281        || arguments.named_child_count() != 1
14282        || arguments.child_count() != 3
14283        || arguments
14284            .child(0)
14285            .is_none_or(|open| open.kind() != "(" || open.is_missing())
14286        || arguments
14287            .child(2)
14288            .is_none_or(|close| close.kind() != ")" || close.is_missing())
14289    {
14290        return None;
14291    }
14292    let inner = arguments.named_child(0)?;
14293    let close_index = match inner.kind() {
14294        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
14295        // tree-sitter completes the C++ cast production with a zero-width
14296        // value after the live close parenthesis.
14297        "cast_expression" => inner.child_count().checked_sub(2)?,
14298        _ => return None,
14299    };
14300    (inner
14301        .child(0)
14302        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
14303        && inner
14304            .child(close_index)
14305            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
14306    .then_some(inner)
14307}
14308
14309fn cpp_prototype_macro_candidate_from_init_declaration(
14310    declaration: Node<'_>,
14311    source: &str,
14312) -> Option<PrototypeMacroCandidate> {
14313    let init = declaration
14314        .child_by_field_name("declarator")
14315        .filter(|declarator| declarator.kind() == "init_declarator")?;
14316    let malformed_declarator = init.child_by_field_name("declarator")?;
14317    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
14318        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
14319    } else {
14320        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
14321            return None;
14322        }
14323        let declared_name_error = init
14324            .prev_named_sibling()
14325            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
14326        let declared_name = declared_name_error
14327            .named_child(0)
14328            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
14329        (declared_name, malformed_declarator)
14330    };
14331    let arguments = init
14332        .child_by_field_name("value")
14333        .filter(|value| value.kind() == "argument_list")?;
14334    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
14335    let semicolon = cpp_direct_semicolon(declaration)?;
14336    let return_type = declaration.child_by_field_name("type")?;
14337    if return_type.end_byte() > declared_name.start_byte()
14338        || declared_name.end_byte() > macro_name.start_byte()
14339        || macro_name.end_byte() > arguments.start_byte()
14340        || arguments.end_byte() > semicolon.start_byte()
14341    {
14342        return None;
14343    }
14344    Some(PrototypeMacroCandidate {
14345        run_start: declaration.start_byte(),
14346        identifier_start: macro_name.start_byte(),
14347        inner_open_start: inner.start_byte(),
14348        inner_close_end: inner.end_byte(),
14349        outer_close_end: arguments.end_byte(),
14350        semicolon_end: semicolon.end_byte(),
14351    })
14352}
14353
14354fn cpp_prototype_macro_candidate_from_qualified_declaration(
14355    declaration: Node<'_>,
14356    source: &str,
14357) -> Option<PrototypeMacroCandidate> {
14358    let qualified = declaration
14359        .child_by_field_name("declarator")
14360        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
14361    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
14362    let open_error = qualified
14363        .next_named_sibling()
14364        .filter(|next| next.kind() == "ERROR")?;
14365    let close_error = last_named_child(declaration)
14366        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
14367    if open_error.child_count() < 3
14368        || open_error
14369            .child(0)
14370            .is_none_or(|open| open.kind() != "(" || open.is_missing())
14371        || open_error
14372            .child(1)
14373            .is_none_or(|open| open.kind() != "(" || open.is_missing())
14374        || close_error.child_count() != 2
14375        || close_error
14376            .child(0)
14377            .is_none_or(|close| close.kind() != ")" || close.is_missing())
14378        || close_error
14379            .child(1)
14380            .is_none_or(|close| close.kind() != ")" || close.is_missing())
14381    {
14382        return None;
14383    }
14384    let inner_open = open_error.child(1)?;
14385    let inner_close = close_error.child(0)?;
14386    let outer_close = close_error.child(1)?;
14387    let semicolon = cpp_direct_semicolon(declaration)?;
14388    let return_type = declaration.child_by_field_name("type")?;
14389    if return_type.end_byte() > qualified.start_byte()
14390        || macro_name.end_byte() > open_error.start_byte()
14391        || inner_open.start_byte() > inner_close.end_byte()
14392        || inner_close.end_byte() > outer_close.start_byte()
14393        || outer_close.end_byte() > semicolon.start_byte()
14394    {
14395        return None;
14396    }
14397    Some(PrototypeMacroCandidate {
14398        run_start: declaration.start_byte(),
14399        identifier_start: macro_name.start_byte(),
14400        inner_open_start: inner_open.start_byte(),
14401        inner_close_end: inner_close.end_byte(),
14402        outer_close_end: outer_close.end_byte(),
14403        semicolon_end: semicolon.end_byte(),
14404    })
14405}
14406
14407fn cpp_prototype_macro_candidate_from_pointer_expression(
14408    statement: Node<'_>,
14409    source: &str,
14410) -> Option<PrototypeMacroCandidate> {
14411    if statement.kind() != "expression_statement"
14412        || statement.named_child_count() != 1
14413        || !statement.has_error()
14414    {
14415        return None;
14416    }
14417    let expansion = statement
14418        .named_child(0)
14419        .filter(|child| child.kind() == "parameter_pack_expansion")?;
14420    let binary = expansion
14421        .child_by_field_name("pattern")
14422        .filter(|pattern| pattern.kind() == "binary_expression")?;
14423    if binary.child_count() != 3
14424        || binary
14425            .child(1)
14426            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
14427        || expansion
14428            .child(expansion.child_count().checked_sub(1)?)
14429            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
14430    {
14431        return None;
14432    }
14433    let return_type = binary.child_by_field_name("left")?;
14434    let call = binary
14435        .child_by_field_name("right")
14436        .filter(|right| right.kind() == "call_expression")?;
14437    let qualified = call.child_by_field_name("function")?;
14438    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
14439    let arguments = call
14440        .child_by_field_name("arguments")
14441        .filter(|arguments| arguments.kind() == "argument_list")?;
14442    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
14443    let semicolon = cpp_direct_semicolon(statement)?;
14444    if return_type.end_byte() > declared_name.start_byte()
14445        || macro_name.end_byte() > arguments.start_byte()
14446        || arguments.end_byte() > semicolon.start_byte()
14447    {
14448        return None;
14449    }
14450    Some(PrototypeMacroCandidate {
14451        run_start: statement.start_byte(),
14452        identifier_start: macro_name.start_byte(),
14453        inner_open_start: inner.start_byte(),
14454        inner_close_end: inner.end_byte(),
14455        outer_close_end: arguments.end_byte(),
14456        semicolon_end: semicolon.end_byte(),
14457    })
14458}
14459
14460/// Recover only CST shapes whose fields preserve the declaration name, macro
14461/// invocation, nested argument list, and terminator. Arbitrary flattened
14462/// `ERROR` wreckage stays unsupported rather than being interpreted through a
14463/// token or delimiter scan (issue #2932).
14464fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
14465    let candidate = match node.kind() {
14466        "declaration" if node.has_error() => {
14467            cpp_prototype_macro_candidate_from_init_declaration(node, source)
14468                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
14469        }
14470        "expression_statement" => {
14471            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
14472        }
14473        _ => None,
14474    };
14475    candidate.into_iter().collect()
14476}
14477
14478fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
14479    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
14480        return false;
14481    }
14482    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
14483        return false;
14484    }
14485    let Some(declarator) = (if node.kind() == "function_definition" {
14486        node.child_by_field_name("declarator")
14487            .and_then(extract_function_declarator)
14488    } else {
14489        node.named_child(0)
14490            .filter(|child| child.kind() == "function_declarator")
14491    }) else {
14492        return false;
14493    };
14494    let Some(name) = cpp_function_declarator_name_node(declarator) else {
14495        return false;
14496    };
14497    declarator.start_byte() == node.start_byte()
14498        && name.kind() == "identifier"
14499        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
14500}
14501
14502/// Reparse a fragmented class-body interior while preserving its original byte
14503/// and line offsets, confined to the region by tree-sitter included ranges.
14504///
14505/// This used to materialize the region's whole file prefix as whitespace and
14506/// make the lexer walk it, the technique #1309 replaced on the other reparse
14507/// path: O(file) per fragmented-class recovery, on files that are already
14508/// error-recovered and already slow (#2788). Included ranges give the parser
14509/// the same view -- the region's bytes, at their original offsets and
14510/// line/column positions -- without materializing or lexing anything before it.
14511///
14512/// The equality of the two views is the claim, so a debug build parses both and
14513/// asserts the trees agree node for node.
14514fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
14515    let region = cpp_reparse_region_items(source, start, end);
14516
14517    #[cfg(debug_assertions)]
14518    assert_eq!(
14519        region.as_ref().map(cpp_tree_shape),
14520        cpp_reparse_padded_class_body(source, start, end)
14521            .as_ref()
14522            .map(cpp_tree_shape),
14523        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
14524         prefix produces"
14525    );
14526
14527    region
14528}
14529
14530/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
14531/// replaces, kept as the oracle a debug build asserts every region reparse
14532/// against and as the release-mode parity tests' reference (#2788).
14533#[cfg(any(debug_assertions, test))]
14534fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
14535    if start >= end {
14536        // The included-range parser refuses an empty region; the padded one
14537        // returned a tree holding nothing, which every caller read as "no
14538        // members here".
14539        return None;
14540    }
14541    let bytes = source.as_bytes();
14542    let prefix = bytes.get(..start)?;
14543    let interior = bytes.get(start..end)?;
14544    let mut padded = Vec::with_capacity(end);
14545    padded.extend(
14546        prefix
14547            .iter()
14548            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
14549    );
14550    padded.extend_from_slice(interior);
14551    let padded = String::from_utf8(padded).ok()?;
14552    let mut parser = Parser::new();
14553    parser
14554        .set_language(&tree_sitter_cpp::LANGUAGE.into())
14555        .ok()?;
14556    parser.parse(&padded, None)
14557}
14558
14559/// Every node of `tree` in preorder, by kind, span and position, which is what
14560/// two reparses of one region have to agree on for their callers to read the
14561/// same declarations out of either (#2788).
14562#[cfg(any(debug_assertions, test))]
14563fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
14564    let mut shape = Vec::new();
14565    let mut cursor = tree.root_node().walk();
14566    let mut stack = vec![tree.root_node()];
14567    while let Some(node) = stack.pop() {
14568        shape.push((
14569            node.kind(),
14570            node.start_byte(),
14571            node.end_byte(),
14572            node.start_position().row,
14573            node.start_position().column,
14574            node.is_named(),
14575            node.is_missing(),
14576        ));
14577        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
14578        stack.extend(children.into_iter().rev());
14579    }
14580    shape
14581}
14582
14583/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
14584/// reparsed interior is indexed only when every top-level named node is a
14585/// well-formed C++ item (or a comment) and at least one real item is present.
14586/// Expression/statement soup surfaces as a top-level `ERROR` or
14587/// `expression_statement`, neither of which is an item kind, so it is rejected.
14588///
14589/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
14590/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
14591/// swallowed by a preceding dangling sentinel) reparses to a real
14592/// `namespace_definition` whose body still holds a bogus `function_definition`,
14593/// so the subtree legitimately carries an error. Container items are admitted
14594/// even with an internal error; the inner bogus function is recovered recursively
14595/// when `visit_function_definition` walks it. Each recursion strips at least one
14596/// leading sentinel, so the region strictly shrinks and recovery terminates.
14597///
14598/// A top-level `function_definition` is the one place we stay strict: it is
14599/// admitted only when it is clean or is itself a sentinel candidate. A function
14600/// that has an error and is not a sentinel is a real callable with a broken body,
14601/// so we refuse the whole reparse and let the ordinary path handle it (preserving
14602/// its real return type rather than re-deriving an implicit one).
14603fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
14604    let mut cursor = root.walk();
14605    let mut saw_item = false;
14606    for child in root.named_children(&mut cursor) {
14607        match child.kind() {
14608            "comment" => {}
14609            "function_definition" => {
14610                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
14611                    return false;
14612                }
14613                saw_item = true;
14614            }
14615            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
14616            _ => return false,
14617        }
14618    }
14619    saw_item
14620}
14621
14622/// Robustness gate for a reparsed fragmented multiple-base export class body
14623/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
14624/// kinds a class body produces when reparsed at translation-unit scope: the
14625/// access-specifier label preceding the first member surfaces as a
14626/// `labeled_statement` wrapping that member, and members surface as
14627/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
14628/// Statement or expression soup surfaces as other top-level kinds and is rejected,
14629/// so only a genuinely member-shaped body is ever re-owned as members; anything
14630/// ambiguous falls back to indexing the class alone.
14631fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
14632    if node.kind() != "ERROR" {
14633        return false;
14634    }
14635    let mut stack = Vec::new();
14636    let mut saw_function_declarator = false;
14637    let mut cursor = node.walk();
14638    for child in node.named_children(&mut cursor) {
14639        stack.push(child);
14640    }
14641    while let Some(current) = stack.pop() {
14642        match current.kind() {
14643            // Tree-sitter may wrap adjacent copy-control declarations in a
14644            // nested ERROR. Keep descending only through ERROR wrappers; the
14645            // actual declaration payload must be a function_declarator.
14646            "ERROR" => {
14647                let mut cursor = current.walk();
14648                stack.extend(current.named_children(&mut cursor));
14649            }
14650            "function_declarator" => saw_function_declarator = true,
14651            _ => return false,
14652        }
14653    }
14654    saw_function_declarator
14655}
14656
14657fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
14658    if node.kind() != "ERROR" {
14659        return false;
14660    }
14661    let mut cursor = node.walk();
14662    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
14663    let [explicit, constructor_error, destructor] = named.as_slice() else {
14664        return false;
14665    };
14666    let Some(constructor) = constructor_error.named_child(0) else {
14667        return false;
14668    };
14669    let Some(constructor_name) =
14670        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
14671    else {
14672        return false;
14673    };
14674    let Some(destructor_name) =
14675        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
14676    else {
14677        return false;
14678    };
14679    let Some(destroyed_type) = destructor_name.named_child(0) else {
14680        return false;
14681    };
14682    explicit.kind() == "explicit_function_specifier"
14683        && constructor_error.kind() == "ERROR"
14684        && constructor_error.named_child_count() == 1
14685        && constructor.kind() == "function_declarator"
14686        && constructor_name.kind() == "identifier"
14687        && destructor.kind() == "function_declarator"
14688        && destructor_name.kind() == "destructor_name"
14689        && destroyed_type.kind() == "identifier"
14690        && node_text(constructor_name, source) == node_text(destroyed_type, source)
14691}
14692
14693fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
14694    if node.kind() != "compound_statement" {
14695        return false;
14696    }
14697    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
14698        return false;
14699    };
14700    if prefix.kind() == "labeled_statement"
14701        && prefix.named_child(0).is_some_and(|label| {
14702            matches!(
14703                node_text(label, source).trim(),
14704                "public" | "private" | "protected"
14705            )
14706        })
14707    {
14708        return prefix.named_children(&mut prefix.walk()).any(|child| {
14709            child.kind() == "declaration"
14710                && child.has_error()
14711                && child
14712                    .named_children(&mut child.walk())
14713                    .any(cpp_reparsed_member_error_is_indexable)
14714        });
14715    }
14716    // A malformed constructor initializer can be split into a declaration
14717    // followed by its compound body when the class prefix already contains
14718    // realistic members. Keep this admission tied to that exact structured
14719    // declaration/error/body chain rather than accepting arbitrary blocks.
14720    prefix.kind() == "declaration"
14721        && prefix.has_error()
14722        && prefix
14723            .named_children(&mut prefix.walk())
14724            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
14725}
14726
14727fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
14728    if !cpp_reparsed_member_error_is_indexable(node) {
14729        return false;
14730    }
14731    let Some(preproc) = node.next_named_sibling() else {
14732        return false;
14733    };
14734    preproc.kind() == "preproc_if"
14735        && preproc.has_error()
14736        && preproc
14737            .named_children(&mut preproc.walk())
14738            .any(|child| child.kind() == "expression_statement" && child.has_error())
14739        && preproc
14740            .next_named_sibling()
14741            .is_some_and(|body| body.kind() == "compound_statement")
14742}
14743
14744/// Return a function body whose braces and ownership are explicit in the
14745/// reparsed class-member tree. An error below a real function envelope is
14746/// recoverable by the ordinary function visitor; a missing/deferred body is
14747/// not, because accepting it would let statement soup masquerade as a member.
14748fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
14749    if node.kind() != "function_definition" {
14750        return None;
14751    }
14752    let body = node.child_by_field_name("body")?;
14753    if body.kind() != "compound_statement" {
14754        return None;
14755    }
14756    let open = body.child(0)?;
14757    let close = body.child(body.child_count().checked_sub(1)?)?;
14758    if open.kind() != "{"
14759        || open.is_missing()
14760        || close.kind() != "}"
14761        || close.is_missing()
14762        || close.end_byte() != body.end_byte()
14763        || body.end_byte() != node.end_byte()
14764    {
14765        return None;
14766    }
14767    Some(body)
14768}
14769
14770fn cpp_reparsed_member_function_errors_are_in_body(
14771    node: Node<'_>,
14772    body: Node<'_>,
14773    source: &str,
14774) -> bool {
14775    let mut cursor = node.walk();
14776    node.children(&mut cursor).all(|child| {
14777        same_node(child, body)
14778            || cpp_reparsed_member_attribute_error(child, source)
14779            || cpp_reparsed_member_signature_identifier_errors(child)
14780            || (!child.has_error() && !child.is_error() && !child.is_missing())
14781    })
14782}
14783
14784/// A complete callable can still carry parser errors in its signature when a
14785/// project annotation is not part of the C++ grammar (`nonneg int`,
14786/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
14787/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
14788/// leaves inside the already-proven callable envelope; structured statements,
14789/// literals, missing tokens, and other malformed signature payload remain
14790/// rejected.
14791fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
14792    if !node.has_error() && !node.is_error() && !node.is_missing() {
14793        return false;
14794    }
14795    let mut stack = vec![node];
14796    let mut saw_error = false;
14797    while let Some(current) = stack.pop() {
14798        if current.is_missing() {
14799            return false;
14800        }
14801        if current.kind() == "ERROR" {
14802            saw_error = true;
14803            let mut cursor = current.walk();
14804            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
14805            if children
14806                .iter()
14807                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
14808            {
14809                return false;
14810            }
14811            stack.extend(children);
14812            continue;
14813        }
14814        let mut cursor = current.walk();
14815        stack.extend(current.children(&mut cursor));
14816    }
14817    saw_error
14818}
14819
14820fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
14821    node.kind() == "ERROR"
14822        && node.named_child_count() == 1
14823        && node.named_child(0).is_some_and(|attribute| {
14824            attribute.kind() == "identifier"
14825                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
14826        })
14827}
14828
14829/// A C++ attribute placed between a member's declarator and body can make
14830/// tree-sitter expose the callable as
14831/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
14832/// Keep this admission tied to that exact node geometry. In particular, an
14833/// arbitrary ERROR or identifier before a compound statement is not enough.
14834fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
14835    let Some(body) = cpp_reparsed_member_function_body(node) else {
14836        return false;
14837    };
14838    let mut cursor = node.walk();
14839    let named = node
14840        .named_children(&mut cursor)
14841        .filter(|child| child.kind() != "comment")
14842        .collect::<Vec<_>>();
14843    let [type_node, error, attribute, body_node] = named.as_slice() else {
14844        return false;
14845    };
14846    if !same_node(*body_node, body)
14847        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
14848        || attribute.kind() != "identifier"
14849        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
14850        || error.kind() != "ERROR"
14851        || error.named_child_count() != 1
14852    {
14853        return false;
14854    }
14855    error
14856        .named_child(0)
14857        .is_some_and(cpp_reparsed_attribute_callable_declarator)
14858}
14859
14860fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
14861    cpp_structured_type_path(node, source).is_some()
14862        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
14863}
14864
14865fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14866    let Some(body) = cpp_reparsed_member_function_body(node) else {
14867        return false;
14868    };
14869    let mut cursor = node.walk();
14870    let named = node
14871        .named_children(&mut cursor)
14872        .filter(|child| child.kind() != "comment")
14873        .collect::<Vec<_>>();
14874    let [friend, return_error, declarator, body_node] = named.as_slice() else {
14875        return false;
14876    };
14877    let Some(return_type) = return_error.named_child(0) else {
14878        return false;
14879    };
14880    same_node(*body_node, body)
14881        && friend.kind() == "type_identifier"
14882        && node_text(*friend, source) == "friend"
14883        && return_error.kind() == "ERROR"
14884        && return_error.named_child_count() == 1
14885        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
14886        && extract_function_declarator(*declarator)
14887            .and_then(cpp_function_declarator_name_node)
14888            .is_some()
14889}
14890
14891fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14892    let Some(body) = cpp_reparsed_member_function_body(node) else {
14893        return false;
14894    };
14895    let mut cursor = node.walk();
14896    let named = node
14897        .named_children(&mut cursor)
14898        .filter(|child| child.kind() != "comment")
14899        .collect::<Vec<_>>();
14900    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
14901        return false;
14902    };
14903    let Some(return_type) = return_error.named_child(0) else {
14904        return false;
14905    };
14906    same_node(*body_node, body)
14907        && prefix
14908            .iter()
14909            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
14910        && attribute.kind() == "type_identifier"
14911        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
14912        && return_error.kind() == "ERROR"
14913        && return_error.named_child_count() == 1
14914        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
14915        && extract_function_declarator(*declarator)
14916            .and_then(cpp_function_declarator_name_node)
14917            .is_some()
14918}
14919
14920/// An included-range reparse that begins inside a malformed class can merge an
14921/// access label and following template member. Tree-sitter then emits the label
14922/// as the `template_type` name, the template parameter list as its arguments,
14923/// an ERROR-wrapped return type, the callable declarator, and its complete
14924/// body. Admit only that exact structured displacement.
14925fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
14926    let Some(body) = cpp_reparsed_member_function_body(node) else {
14927        return false;
14928    };
14929    let mut cursor = node.walk();
14930    let named = node
14931        .named_children(&mut cursor)
14932        .filter(|child| child.kind() != "comment")
14933        .collect::<Vec<_>>();
14934    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
14935        return false;
14936    };
14937    let Some(template_name) = template_type.child_by_field_name("name") else {
14938        return false;
14939    };
14940    let Some(arguments) = template_type.child_by_field_name("arguments") else {
14941        return false;
14942    };
14943    let Some(return_type) = return_error.named_child(0) else {
14944        return false;
14945    };
14946    let mut cursor = template_type.walk();
14947    let template_errors = template_type
14948        .named_children(&mut cursor)
14949        .filter(|child| child.kind() == "ERROR")
14950        .collect::<Vec<_>>();
14951    let [comment_error] = template_errors.as_slice() else {
14952        return false;
14953    };
14954    let mut cursor = comment_error.walk();
14955    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
14956    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
14957        return false;
14958    };
14959    same_node(*body_node, body)
14960        && template_type.kind() == "template_type"
14961        && template_name.kind() == "type_identifier"
14962        && matches!(
14963            node_text(template_name, source).trim(),
14964            "public" | "private" | "protected"
14965        )
14966        && arguments.kind() == "template_argument_list"
14967        && arguments.named_child_count() > 0
14968        && !arguments.has_error()
14969        && !colon.is_named()
14970        && colon.kind() == ":"
14971        && comments.iter().all(|child| child.kind() == "comment")
14972        && !template_keyword.is_named()
14973        && template_keyword.kind() == "template"
14974        && return_error.kind() == "ERROR"
14975        && return_error.named_child_count() == 1
14976        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
14977        && extract_function_declarator(*declarator)
14978            .and_then(cpp_function_declarator_name_node)
14979            .is_some()
14980}
14981
14982/// Return the constructor declaration tree-sitter can merge into an access
14983/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
14984/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
14985/// declaration's apparent type; the callable name must still exactly match the
14986/// recovered class, so unrelated labeled statements are never re-owned.
14987fn cpp_reparsed_preprocessor_constructor<'tree>(
14988    node: Node<'tree>,
14989    class_name: &str,
14990    source: &str,
14991) -> Option<Node<'tree>> {
14992    if node.kind() != "labeled_statement" {
14993        return None;
14994    }
14995    let mut cursor = node.walk();
14996    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
14997    let [label, directive_error, declaration] = named.as_slice() else {
14998        return None;
14999    };
15000    if label.kind() != "statement_identifier"
15001        || !matches!(
15002            node_text(*label, source),
15003            "public" | "private" | "protected"
15004        )
15005        || directive_error.kind() != "ERROR"
15006        || directive_error.child_count() != 1
15007        || directive_error
15008            .child(0)
15009            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
15010        || declaration.kind() != "declaration"
15011        || declaration.named_child_count() != 2
15012    {
15013        return None;
15014    }
15015    let apparent_type = declaration.child_by_field_name("type")?;
15016    if apparent_type.kind() != "type_identifier"
15017        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
15018    {
15019        return None;
15020    }
15021    let declarator = declaration.child_by_field_name("declarator")?;
15022    let function = extract_function_declarator(declarator)?;
15023    let name = cpp_function_declarator_name_node(function)?;
15024    (node_text(name, source) == class_name).then_some(*declaration)
15025}
15026
15027fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
15028    if extract_function_declarator(node)
15029        .and_then(cpp_function_declarator_name_node)
15030        .is_some()
15031    {
15032        return true;
15033    }
15034    node.kind() == "init_declarator"
15035        && node
15036            .child_by_field_name("declarator")
15037            .is_some_and(|declarator| declarator.kind() == "identifier")
15038        && node
15039            .child_by_field_name("value")
15040            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
15041}
15042
15043/// Return true for the constrained/attribute form that tree-sitter splits into
15044/// an ERROR declaration, a preprocessor `requires` clause, and a following
15045/// compound statement. The three nodes must remain immediate named siblings;
15046/// this deliberately does not search source text or skip unrelated statements.
15047fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
15048    if node.kind() != "ERROR" || node.named_child_count() != 3 {
15049        return false;
15050    }
15051    let mut cursor = node.walk();
15052    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15053    let [type_node, function_declarator, attribute] = named.as_slice() else {
15054        return false;
15055    };
15056    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
15057        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
15058        || attribute.kind() != "identifier"
15059        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15060    {
15061        return false;
15062    }
15063    let Some(preproc) =
15064        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
15065    else {
15066        return false;
15067    };
15068    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
15069        .filter(|sibling| sibling.kind() == "compound_statement")
15070    else {
15071        return false;
15072    };
15073    let Some(open) = body.child(0) else {
15074        return false;
15075    };
15076    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
15077        return false;
15078    };
15079    let Some(condition) = preproc.child_by_field_name("condition") else {
15080        return false;
15081    };
15082    let mut cursor = preproc.walk();
15083    let payload = preproc
15084        .named_children(&mut cursor)
15085        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
15086        .collect::<Vec<_>>();
15087    let [requires_statement] = payload.as_slice() else {
15088        return false;
15089    };
15090    let requires_clause = requires_statement.named_child(0);
15091
15092    open.kind() == "{"
15093        && !open.is_missing()
15094        && close.kind() == "}"
15095        && !close.is_missing()
15096        && close.end_byte() == body.end_byte()
15097        && requires_statement.kind() == "expression_statement"
15098        && requires_statement.named_child_count() == 1
15099        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
15100}
15101
15102fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
15103    let mut sibling = node.next_named_sibling();
15104    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
15105        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
15106    }
15107    sibling
15108}
15109
15110fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
15111    let mut sibling = node.prev_named_sibling();
15112    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
15113        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
15114    }
15115    sibling
15116}
15117
15118fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
15119    let Some(preproc) =
15120        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
15121    else {
15122        return false;
15123    };
15124    let Some(error) =
15125        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
15126    else {
15127        return false;
15128    };
15129    cpp_reparsed_attribute_requires_error(error, source)
15130}
15131
15132fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
15133    node: Node<'tree>,
15134    source: &str,
15135) -> Option<Node<'tree>> {
15136    if node.kind() != "ERROR" {
15137        return None;
15138    }
15139    let mut cursor = node.walk();
15140    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15141    let [parameter, macro_name, message] = named.as_slice() else {
15142        return None;
15143    };
15144    let parameter_name = parameter.named_child(0)?;
15145    (parameter.kind() == "type_parameter_declaration"
15146        && parameter_name.kind() == "type_identifier"
15147        && macro_name.kind() == "type_identifier"
15148        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
15149        && message.kind() == "string_literal")
15150        .then_some(parameter_name)
15151}
15152
15153/// Recognize the alternate constraint-macro prefix where tree-sitter retains
15154/// the complete qualified constraint as a fourth child instead of moving it
15155/// into the following function. Keep the gate tied to a two-type template
15156/// constraint that names the declared type parameter.
15157fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
15158    node: Node<'tree>,
15159    source: &str,
15160) -> Option<Node<'tree>> {
15161    if node.kind() != "ERROR" {
15162        return None;
15163    }
15164    let mut cursor = node.walk();
15165    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15166    let [parameter, macro_name, message, constraint] = named.as_slice() else {
15167        return None;
15168    };
15169    let parameter_name = parameter.named_child(0)?;
15170    let constraint_scope = constraint.child_by_field_name("scope")?;
15171    let constraint_template = constraint.child_by_field_name("name")?;
15172    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
15173    let mut argument_cursor = constraint_arguments.walk();
15174    let constraint_types = constraint_arguments
15175        .named_children(&mut argument_cursor)
15176        .collect::<Vec<_>>();
15177    if parameter.kind() != "type_parameter_declaration"
15178        || parameter_name.kind() != "type_identifier"
15179        || macro_name.kind() != "type_identifier"
15180        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
15181        || message.kind() != "string_literal"
15182        || constraint.kind() != "qualified_identifier"
15183        || constraint_scope.kind() != "namespace_identifier"
15184        || !matches!(
15185            constraint_template.kind(),
15186            "template_function" | "template_type"
15187        )
15188        || !matches!(constraint_types.as_slice(), [left, right]
15189            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
15190        || constraint_arguments.has_error()
15191    {
15192        return None;
15193    }
15194    let parameter_text = node_text(parameter_name, source);
15195    let mut stack = constraint_types;
15196    while let Some(current) = stack.pop() {
15197        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
15198            return Some(parameter_name);
15199        }
15200        let mut cursor = current.walk();
15201        stack.extend(current.named_children(&mut cursor));
15202    }
15203    None
15204}
15205
15206fn cpp_reparsed_template_macro_companion_is_indexable(
15207    node: Node<'_>,
15208    parameter_name: Node<'_>,
15209    source: &str,
15210) -> bool {
15211    let Some(body) = cpp_reparsed_member_function_body(node) else {
15212        return false;
15213    };
15214    let mut cursor = node.walk();
15215    let named = node
15216        .named_children(&mut cursor)
15217        .filter(|child| child.kind() != "comment")
15218        .collect::<Vec<_>>();
15219    let [
15220        constraint,
15221        close_error,
15222        storage,
15223        return_error,
15224        declarator,
15225        body_node,
15226    ] = named.as_slice()
15227    else {
15228        return false;
15229    };
15230    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
15231        return false;
15232    };
15233    let Some(constraint_template) = constraint.child_by_field_name("name") else {
15234        return false;
15235    };
15236    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
15237        return false;
15238    };
15239    let Some(return_type) = return_error.named_child(0) else {
15240        return false;
15241    };
15242    let mut cursor = constraint_arguments.walk();
15243    let constraint_types = constraint_arguments
15244        .named_children(&mut cursor)
15245        .collect::<Vec<_>>();
15246    same_node(*body_node, body)
15247        && constraint.kind() == "qualified_identifier"
15248        && constraint_scope.kind() == "namespace_identifier"
15249        && constraint_template.kind() == "template_type"
15250        && matches!(constraint_types.as_slice(), [left, right]
15251            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
15252        && !constraint_arguments.has_error()
15253        && close_error.kind() == "ERROR"
15254        && close_error.named_child_count() == 0
15255        && storage.kind() == "storage_class_specifier"
15256        && return_error.kind() == "ERROR"
15257        && return_error.named_child_count() == 1
15258        && return_type.kind() == "identifier"
15259        && node_text(return_type, source) == node_text(parameter_name, source)
15260        && extract_function_declarator(*declarator)
15261            .and_then(cpp_function_declarator_name_node)
15262            .is_some()
15263}
15264
15265fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
15266    node: Node<'tree>,
15267    parameter_name: Node<'_>,
15268    source: &str,
15269) -> Option<Node<'tree>> {
15270    let body = cpp_reparsed_member_function_body(node)?;
15271    let constraint = node.child_by_field_name("type")?;
15272    let constraint_template = constraint.child_by_field_name("name")?;
15273    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
15274    let mut argument_cursor = constraint_arguments.walk();
15275    let constraint_types = constraint_arguments
15276        .named_children(&mut argument_cursor)
15277        .collect::<Vec<_>>();
15278    if constraint.kind() != "qualified_identifier"
15279        || constraint_template.kind() != "template_type"
15280        || !matches!(constraint_types.as_slice(), [left, right]
15281            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
15282        || constraint_arguments.has_error()
15283        || node
15284            .child_by_field_name("body")
15285            .is_none_or(|candidate| !same_node(candidate, body))
15286    {
15287        return None;
15288    }
15289
15290    let mut cursor = node.walk();
15291    let recovery_errors = node
15292        .named_children(&mut cursor)
15293        .filter(|child| child.kind() == "ERROR")
15294        .collect::<Vec<_>>();
15295    if !recovery_errors
15296        .iter()
15297        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
15298        || !recovery_errors.iter().all(|error| {
15299            error.named_child_count() == 0
15300                || cpp_reparsed_constraint_macro_error(*error, source)
15301                || (error.named_child_count() == 1
15302                    && error
15303                        .named_child(0)
15304                        .is_some_and(|child| child.kind() == "function_declarator"))
15305        })
15306    {
15307        return None;
15308    }
15309
15310    let parameter_text = node_text(parameter_name, source);
15311    let mut declarators = node
15312        .child_by_field_name("declarator")
15313        .and_then(extract_function_declarator)
15314        .into_iter()
15315        .collect::<Vec<_>>();
15316    for error in recovery_errors {
15317        let mut stack = vec![error];
15318        while let Some(current) = stack.pop() {
15319            if current.kind() == "function_declarator" {
15320                declarators.push(current);
15321            }
15322            let mut cursor = current.walk();
15323            stack.extend(current.named_children(&mut cursor));
15324        }
15325    }
15326    declarators.into_iter().find(|declarator| {
15327        cpp_function_declarator_name_node(*declarator)
15328            .is_some_and(|name| name.kind() == "identifier")
15329            && declarator
15330                .child_by_field_name("parameters")
15331                .is_some_and(|parameters| {
15332                    parameters
15333                        .named_children(&mut parameters.walk())
15334                        .filter_map(|parameter| parameter.child_by_field_name("type"))
15335                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
15336                })
15337    })
15338}
15339
15340fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
15341    node: Node<'_>,
15342    parameter_name: Node<'_>,
15343    source: &str,
15344) -> bool {
15345    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
15346}
15347
15348fn cpp_reparsed_template_macro_function_companion_is_indexable(
15349    node: Node<'_>,
15350    parameter_name: Node<'_>,
15351    source: &str,
15352) -> bool {
15353    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
15354        return false;
15355    }
15356    let Some(return_type) = node.child_by_field_name("type") else {
15357        return false;
15358    };
15359    let Some(function_declarator) = node
15360        .child_by_field_name("declarator")
15361        .and_then(extract_function_declarator)
15362    else {
15363        return false;
15364    };
15365    if cpp_function_declarator_name_node(function_declarator).is_none()
15366        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
15367    {
15368        return false;
15369    }
15370    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
15371        return false;
15372    };
15373    let parameter_text = node_text(parameter_name, source);
15374    parameters
15375        .named_children(&mut parameters.walk())
15376        .any(|parameter| {
15377            parameter
15378                .child_by_field_name("type")
15379                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
15380        })
15381}
15382
15383fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
15384    if node.kind() != "ERROR" {
15385        return false;
15386    }
15387    let mut stack = vec![node];
15388    while let Some(current) = stack.pop() {
15389        let macro_shape = match current.kind() {
15390            "call_expression" => current
15391                .child_by_field_name("function")
15392                .zip(current.child_by_field_name("arguments")),
15393            "init_declarator" => current
15394                .child_by_field_name("declarator")
15395                .zip(current.child_by_field_name("value")),
15396            _ => None,
15397        };
15398        if let Some((name, arguments)) = macro_shape
15399            && name.kind() == "identifier"
15400            && arguments.kind() == "argument_list"
15401            && arguments.named_child_count() >= 2
15402            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15403        {
15404            return true;
15405        }
15406        let mut cursor = current.walk();
15407        stack.extend(current.named_children(&mut cursor));
15408    }
15409    false
15410}
15411
15412fn cpp_recovered_template_macro_constructor<'tree>(
15413    node: Node<'tree>,
15414    source: &str,
15415) -> Option<(Node<'tree>, Node<'tree>)> {
15416    let mut prefix = node.prev_named_sibling()?;
15417    while prefix.kind() == "comment" {
15418        prefix = prefix.prev_named_sibling()?;
15419    }
15420    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
15421    let parameter = parameter_name
15422        .parent()
15423        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
15424    let declarator =
15425        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
15426    Some((declarator, parameter))
15427}
15428
15429fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
15430    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
15431        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
15432            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
15433                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
15434                    function,
15435                    parameter_name,
15436                    source,
15437                )
15438        });
15439    }
15440    let Some(parameter_name) =
15441        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
15442    else {
15443        return false;
15444    };
15445    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
15446        cpp_reparsed_template_macro_function_companion_is_indexable(
15447            function,
15448            parameter_name,
15449            source,
15450        )
15451    })
15452}
15453
15454fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15455    let function_name = node
15456        .child_by_field_name("declarator")
15457        .and_then(extract_function_declarator)
15458        .and_then(cpp_function_declarator_name_node);
15459    if let Some(body) = cpp_reparsed_member_function_body(node)
15460        && function_name.is_some()
15461        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
15462    {
15463        return true;
15464    }
15465    cpp_reparsed_attribute_member_function(node, source)
15466        || cpp_reparsed_friend_function_is_indexable(node, source)
15467        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
15468        || cpp_reparsed_access_template_function_is_indexable(node, source)
15469        || cpp_recovered_template_macro_constructor(node, source).is_some()
15470}
15471
15472/// Recognize the three top-level nodes produced when an unknown attribute
15473/// macro separates an inline member's declarator from its body in a reparsed
15474/// class interior: an errorful declaration with a missing semicolon, the macro
15475/// call expression, and the complete compound body. Their adjacency and exact
15476/// structured shapes prove one recoverable member envelope; arbitrary calls or
15477/// blocks do not pass this gate.
15478fn cpp_reparsed_macro_attribute_member_sequence(
15479    children: &[Node<'_>],
15480    index: usize,
15481    source: &str,
15482) -> bool {
15483    let Some(prefix) = children.get(index).copied() else {
15484        return false;
15485    };
15486    let declaration = if prefix.kind() == "labeled_statement" {
15487        prefix
15488            .named_child(prefix.named_child_count().saturating_sub(1))
15489            .filter(|child| child.kind() == "declaration")
15490    } else {
15491        (prefix.kind() == "declaration").then_some(prefix)
15492    };
15493    let Some(declaration) = declaration else {
15494        return false;
15495    };
15496    if !declaration.has_error()
15497        || declaration
15498            .child_by_field_name("declarator")
15499            .and_then(extract_function_declarator)
15500            .and_then(cpp_function_declarator_name_node)
15501            .is_none()
15502    {
15503        return false;
15504    }
15505    let Some(attribute_statement) = children.get(index + 1).copied() else {
15506        return false;
15507    };
15508    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
15509        .then(|| attribute_statement.named_child(0))
15510        .flatten()
15511        .filter(|child| child.kind() == "call_expression")
15512    else {
15513        return false;
15514    };
15515    let Some(attribute_name) = attribute_call
15516        .child_by_field_name("function")
15517        .filter(|function| function.kind() == "identifier")
15518        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
15519    else {
15520        return false;
15521    };
15522    if !cpp_export_macro_token(&attribute_name) {
15523        return false;
15524    }
15525    let Some(body) = children.get(index + 2).copied() else {
15526        return false;
15527    };
15528    body.kind() == "compound_statement"
15529        && body.child(0).is_some_and(|open| open.kind() == "{")
15530        && body
15531            .child(body.child_count().saturating_sub(1))
15532            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
15533        && declaration.end_byte() <= attribute_statement.start_byte()
15534        && attribute_statement.end_byte() <= body.start_byte()
15535}
15536
15537/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
15538/// types and specifiers followed by a declarator, over and over, with nothing
15539/// left over. A string-argument attribute macro is what strands them there, and
15540/// the walk indexes exactly what this reads, so admitting the region is safe
15541/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
15542/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
15543/// the whole class body.
15544fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
15545    if node.kind() != "ERROR" {
15546        return false;
15547    }
15548    let run = stranded_declaration_run(node, source);
15549    run.complete && !run.declarations.is_empty()
15550}
15551
15552fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
15553    let mut cursor = root.walk();
15554    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
15555    let mut saw_member = false;
15556    let mut index = 0;
15557    while index < children.len() {
15558        let child = children[index];
15559        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
15560            saw_member = true;
15561            index += 3;
15562            continue;
15563        }
15564        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
15565            let Some(tree) = cpp_reparse_fragmented_class_body(
15566                source,
15567                fragmented.reparse_start,
15568                fragmented.reparse_end,
15569            ) else {
15570                return false;
15571            };
15572            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
15573                return false;
15574            }
15575            saw_member = true;
15576            index += 1;
15577            while index < children.len()
15578                && children[index].end_byte() <= fragmented.class_range.end_byte
15579            {
15580                index += 1;
15581            }
15582            continue;
15583        }
15584        match child.kind() {
15585            "comment" => {}
15586            "labeled_statement" => saw_member = true,
15587            "function_definition" => {
15588                if child.has_error()
15589                    && !cpp_reparsed_member_function_is_indexable(child, source)
15590                    && cpp_sentinel_macro_region(child, source).is_none()
15591                {
15592                    return false;
15593                }
15594                saw_member = true;
15595            }
15596            // The attribute a string-argument macro leaves as a call statement
15597            // of its own. It declares nothing; the member it decorated is the
15598            // sibling after it, checked on its own turn.
15599            "expression_statement" if is_string_attribute_macro_statement(child) => {}
15600            "ERROR"
15601                if (cpp_reparsed_member_error_is_indexable(child)
15602                    || cpp_reparsed_adjacent_copy_control_error(child, source)
15603                    || cpp_reparsed_stranded_member_error(child, source))
15604                    && (child
15605                        .next_named_sibling()
15606                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
15607                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
15608            {
15609                saw_member = true;
15610            }
15611            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
15612                saw_member = true;
15613            }
15614            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
15615                saw_member = true;
15616            }
15617            "expression_statement"
15618                if cpp_is_stray_semicolon(child, source)
15619                    && child.prev_named_sibling().is_some_and(|error| {
15620                        cpp_reparsed_member_error_is_indexable(error)
15621                            || cpp_reparsed_adjacent_copy_control_error(error, source)
15622                            || cpp_reparsed_stranded_member_error(error, source)
15623                    }) =>
15624            {
15625                saw_member = true;
15626            }
15627            "compound_statement"
15628                if cpp_reparsed_constructor_body_is_indexable(child, source)
15629                    || cpp_reparsed_attribute_requires_body(child, source) =>
15630            {
15631                saw_member = true;
15632            }
15633            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
15634            _ => return false,
15635        }
15636        index += 1;
15637    }
15638    saw_member
15639}
15640
15641/// Detect the malformed constructor shape that tree-sitter exposes as an
15642/// access-label statement followed by initializer-looking declarations. The
15643/// declarations are not class members: visiting their `location(loc)` and
15644/// `string(s)` function declarators would publish synthetic functions. The
15645/// export-class fallback keeps the original sibling nodes and therefore avoids
15646/// this parser artifact. The returned range identifies the real constructor
15647/// header, which can be reparsed independently as a structured declarator.
15648fn cpp_reparsed_synthetic_initializer_constructor_range(
15649    root: Node<'_>,
15650    class_name: &str,
15651    source: &str,
15652    constructor_end: usize,
15653) -> Option<std::ops::Range<usize>> {
15654    let mut stack = {
15655        let mut cursor = root.walk();
15656        root.named_children(&mut cursor).collect::<Vec<_>>()
15657    };
15658    while let Some(current) = stack.pop() {
15659        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
15660            current,
15661            class_name,
15662            source,
15663            constructor_end,
15664        ) {
15665            return Some(range);
15666        }
15667        if current.kind() == "ERROR" {
15668            let mut cursor = current.walk();
15669            stack.extend(current.named_children(&mut cursor));
15670        }
15671    }
15672    None
15673}
15674
15675/// Recover an inline constructor that a function-like export macro makes
15676/// tree-sitter merge with the following overload. In the reparsed class-body
15677/// region, the access label wraps one declaration whose ERROR contains the
15678/// constructor declarator and its base-initializer/body, while the declaration's
15679/// ordinary declarator is the following overload. Every boundary below comes
15680/// from that CST; no source syntax is reparsed by hand.
15681fn cpp_reparsed_merged_inline_constructor<'tree>(
15682    root: Node<'tree>,
15683    class_name: &str,
15684    source: &str,
15685) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
15686    let mut stack = vec![root];
15687    while let Some(current) = stack.pop() {
15688        if current.kind() != "labeled_statement" {
15689            let mut cursor = current.walk();
15690            stack.extend(current.named_children(&mut cursor));
15691            continue;
15692        }
15693        let declaration = current
15694            .named_children(&mut current.walk())
15695            .find(|child| child.kind() == "declaration")?;
15696        if declaration
15697            .child_by_field_name("type")
15698            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
15699        {
15700            continue;
15701        }
15702        let following = declaration
15703            .child_by_field_name("declarator")
15704            .and_then(extract_function_declarator)
15705            .and_then(cpp_function_declarator_name_node);
15706        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
15707            continue;
15708        }
15709        let mut declaration_cursor = declaration.walk();
15710        let Some(error) = declaration
15711            .named_children(&mut declaration_cursor)
15712            .find(|child| child.kind() == "ERROR")
15713        else {
15714            continue;
15715        };
15716        let mut error_cursor = error.walk();
15717        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
15718        let Some(constructor) = error_children.iter().copied().find(|child| {
15719            child.kind() == "function_declarator"
15720                && cpp_function_declarator_name_node(*child)
15721                    .is_some_and(|name| node_text(name, source).trim() == class_name)
15722        }) else {
15723            continue;
15724        };
15725        let Some(body) = error_children.iter().copied().find_map(|child| {
15726            (child.kind() == "init_declarator")
15727                .then(|| child.child_by_field_name("value"))
15728                .flatten()
15729                .filter(|value| value.kind() == "initializer_list")
15730        }) else {
15731            continue;
15732        };
15733        if constructor.end_byte() > body.start_byte() {
15734            continue;
15735        }
15736        return Some((constructor.start_byte()..body.end_byte(), body));
15737    }
15738    None
15739}
15740
15741fn cpp_reparsed_synthetic_initializer_constructor(
15742    node: Node<'_>,
15743    class_name: &str,
15744    source: &str,
15745    constructor_end: usize,
15746) -> Option<std::ops::Range<usize>> {
15747    if node.kind() != "labeled_statement" {
15748        return None;
15749    }
15750    let mut cursor = node.walk();
15751    let named = node
15752        .named_children(&mut cursor)
15753        .filter(|child| child.kind() != "comment")
15754        .collect::<Vec<_>>();
15755    let label = named.first()?;
15756    if label.kind() != "statement_identifier"
15757        || !matches!(
15758            node_text(*label, source).trim(),
15759            "public" | "private" | "protected"
15760        )
15761    {
15762        return None;
15763    }
15764    let call_error_index = named.iter().position(|child| {
15765        if child.kind() != "ERROR" {
15766            return false;
15767        }
15768        let mut stack = vec![*child];
15769        while let Some(current) = stack.pop() {
15770            if current.kind() == "call_expression"
15771                && current
15772                    .child_by_field_name("function")
15773                    .is_some_and(|function| {
15774                        function.kind() == "identifier"
15775                            && node_text(function, source).trim() == class_name
15776                    })
15777            {
15778                return true;
15779            }
15780            let mut cursor = current.walk();
15781            stack.extend(current.named_children(&mut cursor));
15782        }
15783        false
15784    })?;
15785    let constructor_call = {
15786        let mut stack = vec![named[call_error_index]];
15787        let mut found = None;
15788        while let Some(current) = stack.pop() {
15789            if current.kind() == "call_expression"
15790                && current
15791                    .child_by_field_name("function")
15792                    .is_some_and(|function| {
15793                        function.kind() == "identifier"
15794                            && node_text(function, source).trim() == class_name
15795                    })
15796            {
15797                found = Some(current);
15798                break;
15799            }
15800            let mut cursor = current.walk();
15801            stack.extend(current.named_children(&mut cursor));
15802        }
15803        found
15804    };
15805    let constructor_call = constructor_call?;
15806    named.iter().skip(call_error_index + 1).find(|child| {
15807        child.kind() == "declaration" && child.has_error() && {
15808            let mut cursor = child.walk();
15809            child.named_children(&mut cursor).any(|declarator| {
15810                declarator.kind() == "init_declarator"
15811                    && declarator
15812                        .child_by_field_name("declarator")
15813                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
15814                    && declarator
15815                        .child_by_field_name("value")
15816                        .is_some_and(|value| value.kind() == "initializer_list")
15817            })
15818        }
15819    })?;
15820    Some(constructor_call.start_byte()..constructor_end)
15821}
15822
15823fn cpp_reparsed_exact_constructor_declarator<'tree>(
15824    root: Node<'tree>,
15825    start: usize,
15826    class_name: &str,
15827    source: &str,
15828) -> Option<Node<'tree>> {
15829    let mut candidate = None;
15830    let mut stack = vec![root];
15831    while let Some(current) = stack.pop() {
15832        if current.kind() == "function_declarator"
15833            && current.start_byte() == start
15834            && cpp_function_declarator_name_node(current)
15835                .is_some_and(|name| node_text(name, source).trim() == class_name)
15836        {
15837            if candidate.is_some() {
15838                return None;
15839            }
15840            candidate = Some(current);
15841            continue;
15842        }
15843        let mut cursor = current.walk();
15844        stack.extend(current.named_children(&mut cursor));
15845    }
15846    candidate
15847}
15848
15849fn cpp_is_indexable_item_kind(kind: &str) -> bool {
15850    matches!(
15851        kind,
15852        "namespace_definition"
15853            | "class_specifier"
15854            | "struct_specifier"
15855            | "union_specifier"
15856            | "enum_specifier"
15857            | "function_definition"
15858            | "template_declaration"
15859            | "declaration"
15860            | "field_declaration"
15861            | "alias_declaration"
15862            | "static_assert_declaration"
15863            | "type_definition"
15864            | "using_declaration"
15865            | "linkage_specification"
15866            | "preproc_def"
15867            | "preproc_function_def"
15868            | "preproc_include"
15869            | "preproc_if"
15870            | "preproc_ifdef"
15871            | "preproc_call"
15872    )
15873}
15874
15875#[cfg(test)]
15876mod tests {
15877    use super::*;
15878    use crate::adapter::parse_cpp_file;
15879    use brokk_bifrost_core::analyzer::parsed_file::{
15880        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
15881        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
15882    };
15883    use std::fmt::Write;
15884
15885    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
15886        let mut parser = tree_sitter::Parser::new();
15887        parser
15888            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15889            .unwrap();
15890        let tree = parser.parse(source, None).unwrap();
15891        let file = ProjectFile::new(std::env::temp_dir(), name);
15892        parse_cpp_file(&file, source, &tree)
15893    }
15894
15895    #[test]
15896    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
15897        let source = "struct Image { PyObject_HEAD Imaging image; };";
15898        let parsed = parse_cpp_declarations(source, "image.h");
15899        let names = parsed
15900            .declarations()
15901            .iter()
15902            .map(|unit| unit.fq_name())
15903            .collect::<Vec<_>>();
15904
15905        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
15906        assert!(
15907            names.iter().all(|name| name != "Image.Imaging"),
15908            "the pseudo-declarator must not become a field: {names:#?}"
15909        );
15910
15911        let pointer = parse_cpp_declarations(
15912            "struct Image { PyObject_HEAD Imaging *image; };",
15913            "image-pointer.h",
15914        );
15915        let pointer_names = pointer
15916            .declarations()
15917            .iter()
15918            .map(|unit| unit.fq_name())
15919            .collect::<Vec<_>>();
15920        assert!(
15921            pointer_names.iter().any(|name| name == "Image.image"),
15922            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
15923        );
15924        assert!(
15925            pointer_names.iter().all(|name| name != "Image.Imaging"),
15926            "the pointer recovery error must not become a field: {pointer_names:#?}"
15927        );
15928
15929        let unrelated_macro = parse_cpp_declarations(
15930            "struct Image { OTHER_HEAD Imaging other; };",
15931            "image-near-miss.h",
15932        );
15933        let unrelated_names = unrelated_macro
15934            .declarations()
15935            .iter()
15936            .map(|unit| unit.fq_name())
15937            .collect::<Vec<_>>();
15938        assert!(
15939            unrelated_names.iter().all(|name| name != "Image.other"),
15940            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
15941        );
15942    }
15943
15944    #[test]
15945    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
15946        let source = r#"namespace testing {
15947namespace internal {
15948    namespace detail {
15949        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
15950        public:
15951            int value() const { return count_ + 1; }
15952        private:
15953            int count_;
15954        };
15955        class GTEST_API_ [[nodiscard]] OtherReporter {
15956        public:
15957            int value() const { return count_ + 2; }
15958        private:
15959            int count_;
15960        };
15961    }
15962
15963    template <typename T>
15964    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
15965
15966    class TailReporter {};
15967}
15968}
15969"#;
15970        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
15971        let declarations = parsed.declarations();
15972        let mut parser = tree_sitter::Parser::new();
15973        parser
15974            .set_language(&tree_sitter_cpp::LANGUAGE.into())
15975            .unwrap();
15976        let tree = parser.parse(source, None).unwrap();
15977        let tail_start = source.find("TailReporter").expect("tail class");
15978        let tail_node = tree
15979            .root_node()
15980            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
15981            .expect("tail class AST node");
15982        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
15983        assert!(
15984            tree.root_node().has_error(),
15985            "the malformed class must exercise recovery"
15986        );
15987        assert!(index.region_at(tail_start).is_some());
15988        assert_eq!(
15989            index.enclosing_namespace_components(tail_node, source),
15990            ["testing", "internal"]
15991        );
15992        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
15993        let mut recovered_parsed = ParsedFile::new(String::new());
15994        let class_unit = CodeUnit::new_fq(
15995            file.clone(),
15996            CodeUnitType::Class,
15997            "testing",
15998            "ScopedFakeTestPartResultReporter",
15999            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
16000        );
16001        let scope = ScopeInfo {
16002            package_name: "testing".to_string(),
16003            module: None,
16004            class_unit: Some(class_unit),
16005            template_signature: Some("<typename T>".to_string()),
16006            template_metadata: Some(CppTemplateMetadata {
16007                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
16008                primary_fq_name: String::new(),
16009                parameters: Vec::new(),
16010                specialization_arguments: Vec::new(),
16011                alias_target: None,
16012            }),
16013            declarations_are_fields: true,
16014            recovered_specialization_member_scope: true,
16015            visible_using_namespaces: Vec::new(),
16016        };
16017        let mut visitor = CppVisitor {
16018            file: &file,
16019            source,
16020            parsed: &mut recovered_parsed,
16021            c_tag_semantics: false,
16022            recovered_class_sibling_scopes: HashMap::default(),
16023            consumed_fragment_regions: Vec::new(),
16024            orphaned_namespaces: index,
16025            namespace_forward_scans: HashMap::default(),
16026            field_owners: None,
16027            recovery_captures: Vec::new(),
16028            object_macro_fields: HashMap::default(),
16029            ambiguous_object_macro_fields: HashSet::default(),
16030        };
16031        let recovered = visitor
16032            .recovered_namespace_scope(tail_node, &scope)
16033            .expect("the tail must use the stolen namespace scope");
16034        assert_eq!(recovered.package_name, "testing::internal");
16035        assert!(
16036            recovered.class_unit.is_none(),
16037            "recovered namespace declarations cannot retain the malformed class owner"
16038        );
16039        assert!(recovered.template_signature.is_none());
16040        assert!(recovered.template_metadata.is_none());
16041        assert!(!recovered.declarations_are_fields);
16042        assert!(!recovered.recovered_specialization_member_scope);
16043        assert!(
16044            declarations
16045                .iter()
16046                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
16047            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
16048        );
16049        assert!(
16050            declarations
16051                .iter()
16052                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
16053            "the recovered free function remains in its namespace: {declarations:#?}"
16054        );
16055        assert!(
16056            declarations
16057                .iter()
16058                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
16059            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
16060        );
16061        assert!(
16062            declarations.iter().all(|unit| {
16063                !unit
16064                    .short_name()
16065                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
16066            }),
16067            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
16068        );
16069        assert!(
16070            declarations
16071                .iter()
16072                .all(|unit| !unit.identifier().is_empty()),
16073            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
16074        );
16075    }
16076
16077    #[test]
16078    fn object_like_field_macros_materialize_owner_specific_declarations() {
16079        let source = r#"#define PUBLIC_FIELDS int public_value;
16080#define PRIVATE_FIELDS int private_value;
16081#define NOT_A_FIELD_LIST not a declaration
16082
16083struct First {
16084  PUBLIC_FIELDS
16085  PRIVATE_FIELDS
16086};
16087struct Second {
16088  PUBLIC_FIELDS
16089  NOT_A_FIELD_LIST
16090};
16091#undef PUBLIC_FIELDS
16092struct Third {
16093  PUBLIC_FIELDS
16094};
16095"#;
16096        let parsed = parse_cpp_declarations(source, "macro-fields.c");
16097        let fields = parsed
16098            .declarations()
16099            .iter()
16100            .filter(|unit| unit.is_field())
16101            .map(|unit| unit.fq_name())
16102            .collect::<Vec<_>>();
16103
16104        assert!(
16105            fields.contains(&"First.public_value".to_string()),
16106            "{fields:?}"
16107        );
16108        assert!(
16109            fields.contains(&"First.private_value".to_string()),
16110            "{fields:?}"
16111        );
16112        assert!(
16113            fields.contains(&"Second.public_value".to_string()),
16114            "{fields:?}"
16115        );
16116        assert!(
16117            !fields.iter().any(|field| field.contains("not_a_field")),
16118            "malformed macro must fail closed: {fields:?}"
16119        );
16120        assert!(
16121            !fields.iter().any(|field| field.starts_with("Third.")),
16122            "undefined macro must fail closed: {fields:?}"
16123        );
16124    }
16125
16126    #[test]
16127    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
16128        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
16129        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
16130        let mut macros = parsed
16131            .declarations()
16132            .iter()
16133            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
16134            .collect::<Vec<_>>();
16135        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
16136
16137        assert_eq!(macros.len(), 2, "{macros:#?}");
16138        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
16139        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
16140        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
16141        assert_eq!(
16142            parsed.declaration_ranges(macros[1])[0].start_byte,
16143            source.rfind("#define VALUE 2").expect("second definition")
16144        );
16145    }
16146
16147    #[test]
16148    fn identifies_export_macro_class_base_displaced_into_declarator() {
16149        let source = r#"#define PROJECT_API_
16150namespace project {
16151namespace internal {
16152template <typename T>
16153class Base {};
16154}
16155template <typename T>
16156class Wrapper;
16157template <>
16158class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
16159}
16160"#;
16161        let mut parser = tree_sitter::Parser::new();
16162        parser
16163            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16164            .unwrap();
16165        let tree = parser.parse(source, None).unwrap();
16166        let start = source.find("internal::Base<int>").expect("base");
16167        let mut base = tree
16168            .root_node()
16169            .descendant_for_byte_range(start, start + 8)
16170            .expect("base syntax");
16171        while base.kind() != "qualified_identifier" {
16172            base = base.parent().expect("qualified base ancestor");
16173        }
16174        assert!(
16175            is_recovered_exported_class_base_type_node(base, source),
16176            "{}",
16177            tree.root_node().to_sexp()
16178        );
16179    }
16180
16181    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
16182        let mut identities = parsed
16183            .declarations()
16184            .iter()
16185            .filter(|unit| unit.is_function())
16186            .map(|unit| {
16187                (
16188                    unit.fq_name(),
16189                    unit.signature().unwrap_or_default().to_string(),
16190                )
16191            })
16192            .collect::<Vec<_>>();
16193        identities.sort();
16194        identities
16195    }
16196
16197    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
16198    /// prototype macros. Each case is parsed independently so a preceding
16199    /// malformed line cannot flatten the next declaration's fields into one
16200    /// cascading `ERROR`; that shape has no structural proof and must fail
16201    /// closed rather than fall back to a token scan. Parameter names are
16202    /// dropped from signatures just as they are for ordinary C prototypes.
16203    #[test]
16204    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
16205        let cases = [
16206            (
16207                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
16208                "pg_typemap_fit_to_result",
16209                "(VALUE, VALUE)",
16210            ),
16211            (
16212                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
16213                "pg_typemap_result_value",
16214                "(t_typemap *, VALUE, int, int)",
16215            ),
16216            (
16217                "void pg_typemap_mark _(( void * ));",
16218                "pg_typemap_mark",
16219                "(void *)",
16220            ),
16221            (
16222                "void init_pg_type_map _(( void ));",
16223                "init_pg_type_map",
16224                "(void)",
16225            ),
16226            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
16227            (
16228                "extern VALUE pg_extern _(( VALUE, VALUE ));",
16229                "pg_extern",
16230                "(VALUE, VALUE)",
16231            ),
16232            (
16233                "size_t pg_typemap_memsize _(( const void * ));",
16234                "pg_typemap_memsize",
16235                "(const void *)",
16236            ),
16237            (
16238                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
16239                "pg_wrap_socket_io",
16240                "(int, VALUE, VALUE *, int *)",
16241            ),
16242            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
16243            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
16244            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
16245        ];
16246
16247        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
16248            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
16249            assert_eq!(
16250                function_identities(&parsed),
16251                vec![(name.to_string(), signature.to_string())],
16252                "{source}: {:#?}",
16253                parsed.declarations()
16254            );
16255            assert!(
16256                parsed.declarations().iter().all(|unit| !unit.is_field()),
16257                "{source} must not retain the malformed field: {:#?}",
16258                parsed.declarations()
16259            );
16260        }
16261    }
16262
16263    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
16264    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
16265    /// it as a multiplication of a type by a call
16266    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
16267    /// wrapped in an `expression_statement` that `has_error()` only because
16268    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
16269    /// here with a clean preceding field so the prototype remains the
16270    /// parser-owned `expression_statement` this recovery requires.
16271    #[test]
16272    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
16273        let cases = [
16274            (
16275                "PGconn *pg_get_pgconn _(( VALUE ));",
16276                "pg_get_pgconn",
16277                "(VALUE)",
16278            ),
16279            (
16280                "PGresult* pgresult_get _(( VALUE ));",
16281                "pgresult_get",
16282                "(VALUE)",
16283            ),
16284        ];
16285        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
16286            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
16287            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
16288            assert_eq!(
16289                function_identities(&parsed),
16290                vec![(name.to_string(), signature.to_string())],
16291                "{source}: {:#?}",
16292                parsed.declarations()
16293            );
16294            assert_eq!(
16295                parsed
16296                    .declarations()
16297                    .iter()
16298                    .filter(|unit| unit.is_field())
16299                    .map(|unit| unit.fq_name())
16300                    .collect::<Vec<_>>(),
16301                vec!["rb_mPG".to_string()],
16302                "{source}: {:#?}",
16303                parsed.declarations()
16304            );
16305        }
16306    }
16307
16308    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
16309    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
16310    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
16311    /// regions. The witnessed declaration must still become a Function and
16312    /// its type must not survive as the name of a bogus Field. This test does
16313    /// not claim that structurally flattened neighboring prototypes are safe
16314    /// to recover.
16315    #[test]
16316    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
16317        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
16318VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
16319int pg_typemap_fit_to_copy_get                         _(( VALUE ));
16320VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
16321t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
16322VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
16323void pg_typemap_mark                                   _(( void * ));
16324size_t pg_typemap_memsize                              _(( const void * ));
16325void pg_typemap_compact                                _(( void * ));
16326
16327PGconn *pg_get_pgconn                                  _(( VALUE ));
16328t_pg_connection *pg_get_connection                     _(( VALUE ));
16329VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
16330#ifdef __GNUC__
16331__attribute__((format(printf, 3, 4)))
16332#endif
16333NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
16334VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
16335void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
16336
16337
16338VALUE pg_new_result                                    _(( PGresult *, VALUE ));
16339VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
16340PGresult* pgresult_get                                 _(( VALUE ));
16341VALUE pg_result_check                                  _(( VALUE ));
16342VALUE pg_result_clear                                  _(( VALUE ));
16343VALUE pg_tuple_new                                     _(( VALUE, int ));
16344
16345/*
16346 * Fetch the data pointer for the result object
16347 */
16348static inline t_pg_result *
16349pgresult_get_this( VALUE self )
16350{
16351	return RTYPEDDATA_DATA(self);
16352}
16353
16354
16355rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
16356const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
16357rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
16358
16359"#;
16360        let parsed = parse_cpp_declarations(source, "pg.h");
16361        assert!(
16362            function_identities(&parsed)
16363                .iter()
16364                .any(|(name, signature)| name == "pg_typemap_result_value"
16365                    && signature == "(t_typemap *, VALUE, int, int)"),
16366            "the real issue witness must be a Function with its C signature: {:#?}",
16367            parsed.declarations()
16368        );
16369        assert!(
16370            parsed
16371                .declarations()
16372                .iter()
16373                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
16374            "the issue witness must not leave its return type as a Field name: {:#?}",
16375            parsed.declarations()
16376        );
16377    }
16378
16379    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
16380    /// between `explicit` and a constructor name onto the name, producing a
16381    /// `qualified_identifier` whose `::` it had to invent. The declared member
16382    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
16383    /// macro in type position, was already recovered and is the control that
16384    /// both spellings agree.
16385    #[test]
16386    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
16387        let source = r#"class SIMD_4x26 final {
16388   public:
16389      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
16390      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
16391      int m_v;
16392};
16393"#;
16394        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
16395        assert_eq!(
16396            function_identities(&parsed),
16397            vec![
16398                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
16399                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
16400            ],
16401            "{:#?}",
16402            parsed.declarations()
16403        );
16404    }
16405
16406    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
16407    /// `ERROR` holding the wrapped declaration and every declaration after it
16408    /// until the parser recovers. All of them must be indexed, each with the
16409    /// byte range that spells it.
16410    #[test]
16411    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
16412        let source = r#"#include <cstdint>
16413struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
16414    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
16415                     struct llama_model * model,
16416              struct llama_context_params   params),
16417            "use llama_init_from_model instead");
16418    LLAMA_API int32_t llama_tokenize(
16419        const struct llama_vocab * vocab,
16420                      const char * text,
16421                            bool   parse_special);
16422    LLAMA_API int32_t llama_other(int a);
16423"#;
16424        let parsed = parse_cpp_declarations(source, "llama.h");
16425        assert_eq!(
16426            function_identities(&parsed),
16427            vec![
16428                (
16429                    "llama_new_context_with_model".to_string(),
16430                    "(struct llama_model *, struct llama_context_params)".to_string()
16431                ),
16432                ("llama_other".to_string(), "(int)".to_string()),
16433                (
16434                    "llama_tokenize".to_string(),
16435                    "(const struct llama_vocab *, const char *, bool)".to_string()
16436                ),
16437            ],
16438            "{:#?}",
16439            parsed.declarations()
16440        );
16441
16442        // Each recovered declaration owns the source that spells it, so
16443        // navigation lands on the declaration and not on the macro envelope.
16444        for (name, expected) in [
16445            (
16446                "llama_new_context_with_model",
16447                "LLAMA_API struct llama_context * llama_new_context_with_model(",
16448            ),
16449            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
16450            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
16451        ] {
16452            let unit = parsed
16453                .declarations()
16454                .iter()
16455                .find(|unit| unit.is_function() && unit.fq_name() == name)
16456                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
16457            let [range] = parsed.declaration_ranges(unit) else {
16458                panic!("{name} must have exactly one range");
16459            };
16460            let text = &source[range.start_byte..range.end_byte];
16461            assert!(
16462                text.starts_with(expected),
16463                "{name} range is {text:?}, expected it to start with {expected:?}"
16464            );
16465            assert!(
16466                text.ends_with(')') || text.ends_with(';'),
16467                "{name}: {text:?}"
16468            );
16469        }
16470    }
16471
16472    /// Negative controls for the same recovery: a macro invocation whose
16473    /// arguments are not a declaration recovers nothing, whether the parser
16474    /// keeps it clean, reads the arguments as a type, or reads them as a bare
16475    /// declarator.
16476    #[test]
16477    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
16478        for source in [
16479            "int before;\nFOO(1, 2);\nint after;\n",
16480            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
16481            "int before;\nMACRO(int a, int b);\nint after;\n",
16482            "DECLARE_HANDLE(HWND);\nint after;\n",
16483        ] {
16484            let parsed = parse_cpp_declarations(source, "macro-call.h");
16485            assert_eq!(
16486                function_identities(&parsed),
16487                Vec::new(),
16488                "{source:?} must declare no function: {:#?}",
16489                parsed.declarations()
16490            );
16491        }
16492    }
16493
16494    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
16495    /// makes tree-sitter read the macro as the member's type and its argument
16496    /// list as a parenthesized declarator, which then swallows the attributed
16497    /// member and the member written after it. Both are members.
16498    #[test]
16499    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
16500        let source = r#"#include <string_view>
16501namespace Botan {
16502class DL_Group final {
16503   public:
16504      DL_Group() = default;
16505      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
16506      DL_Group(std::string_view pem, int format);
16507      size_t get_p() const;
16508};
16509}
16510"#;
16511        let parsed = parse_cpp_declarations(source, "dl_group.h");
16512        assert_eq!(
16513            function_identities(&parsed),
16514            vec![
16515                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
16516                (
16517                    "Botan.DL_Group.DL_Group".to_string(),
16518                    "(std::string_view)".to_string()
16519                ),
16520                (
16521                    "Botan.DL_Group.DL_Group".to_string(),
16522                    "(std::string_view, int)".to_string()
16523                ),
16524                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
16525            ],
16526            "{:#?}",
16527            parsed.declarations()
16528        );
16529    }
16530
16531    /// #2552 shape 3, export-macro class. The class head macro makes the body a
16532    /// `compound_statement`, so members come from the region reparse, and one
16533    /// string-attribute member used to make that reparse unindexable -- which
16534    /// left the class with no members at all.
16535    #[test]
16536    fn an_export_macro_class_keeps_its_string_attribute_members() {
16537        let source = r#"#include <string_view>
16538namespace Botan {
16539class BOTAN_PUBLIC_API(2, 0) DL_Group final {
16540   public:
16541      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
16542      DL_Group(std::string_view pem, int format);
16543      size_t get_p() const;
16544};
16545}
16546"#;
16547        let parsed = parse_cpp_declarations(source, "dl_group.h");
16548        assert!(
16549            parsed
16550                .declarations()
16551                .iter()
16552                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
16553            "{:#?}",
16554            parsed.declarations()
16555        );
16556        assert_eq!(
16557            function_identities(&parsed),
16558            vec![
16559                (
16560                    "Botan.DL_Group.DL_Group".to_string(),
16561                    "(std::string_view)".to_string()
16562                ),
16563                (
16564                    "Botan.DL_Group.DL_Group".to_string(),
16565                    "(std::string_view, int)".to_string()
16566                ),
16567                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
16568            ],
16569            "{:#?}",
16570            parsed.declarations()
16571        );
16572    }
16573
16574    /// #2552 shape 3, the `= default` variant plus the access-label
16575    /// constructor. The attributed defaulted constructor separates cleanly, but
16576    /// it strands the next member in a bare `ERROR`, and a constructor written
16577    /// with a member-initializer list under `private:` dissolves into
16578    /// expression soup that the reparse recovers from its own source range.
16579    #[test]
16580    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
16581        let source = r#"namespace Botan {
16582class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
16583   public:
16584      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
16585      XMSS_Parameters(int oid, int len);
16586      size_t len() const;
16587
16588   private:
16589      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
16590            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
16591
16592      int m_oid;
16593      int m_wots_oid;
16594      size_t m_element_size;
16595      size_t m_tree_height;
16596};
16597}
16598"#;
16599        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
16600        let constructors = function_identities(&parsed)
16601            .into_iter()
16602            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
16603            .map(|(_, signature)| signature)
16604            .collect::<Vec<_>>();
16605        assert_eq!(
16606            constructors,
16607            vec![
16608                "()".to_string(),
16609                "(int, int)".to_string(),
16610                "(int, int, size_t, size_t)".to_string(),
16611            ],
16612            "{:#?}",
16613            parsed.declarations()
16614        );
16615    }
16616
16617    /// Negative control for the same rule: a real qualified name spells its
16618    /// `::` in the source, so the separator is present rather than MISSING and
16619    /// the out-of-line definition keeps its owner.
16620    #[test]
16621    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
16622        let source = r#"namespace shell {
16623struct Outer {
16624   struct Inner {
16625      Inner(int v);
16626      void run(int v);
16627   };
16628};
16629Outer::Inner::Inner(int v) {}
16630void Outer::Inner::run(int v) {}
16631}
16632"#;
16633        let parsed = parse_cpp_declarations(source, "outer.cpp");
16634        let names = function_identities(&parsed)
16635            .into_iter()
16636            .map(|(fq_name, _)| fq_name)
16637            .collect::<Vec<_>>();
16638        assert!(
16639            names
16640                .iter()
16641                .all(|name| name.starts_with("shell.Outer$Inner.")),
16642            "{names:#?}"
16643        );
16644    }
16645
16646    #[test]
16647    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
16648        let source = r#"namespace control {
16649template <typename T>
16650class AnySpan;
16651template <typename T>
16652class ABSL_ATTRIBUTE_VIEW AnySpan {
16653 public:
16654  int begin() const;
16655};
16656}
16657
16658namespace absl {
16659ABSL_NAMESPACE_BEGIN
16660template <typename T>
16661class ABSL_ATTRIBUTE_VIEW Span {
16662 public:
16663  int begin() const;
16664  int back() const;
16665};
16666
16667int begin();
16668int back();
16669}
16670"#;
16671        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
16672        let declarations = parsed.declarations();
16673        assert!(
16674            declarations
16675                .iter()
16676                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
16677        );
16678        for method in ["begin", "back"] {
16679            assert!(declarations.iter().any(|unit| {
16680                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
16681            }));
16682            assert!(
16683                declarations.iter().any(|unit| {
16684                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
16685                })
16686            );
16687        }
16688        assert!(
16689            declarations
16690                .iter()
16691                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
16692        );
16693        assert!(
16694            declarations
16695                .iter()
16696                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
16697        );
16698        assert!(
16699            declarations
16700                .iter()
16701                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
16702        );
16703    }
16704
16705    #[test]
16706    fn explicit_global_member_definition_has_canonical_package_boundary() {
16707        let source = r#"
16708namespace arangodb::aql {
16709class ExecutionPlan {
16710 public:
16711  template<class... Args> Node* createNode(Args&&... args);
16712};
16713}
16714
16715template<class... Args>
16716Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
16717"#;
16718        let parsed = parse_cpp_declarations(source, "global-member.cpp");
16719
16720        assert!(parsed.declarations().iter().any(|unit| {
16721            unit.is_function()
16722                && unit.package_name() == "arangodb::aql"
16723                && unit.short_name() == "ExecutionPlan.createNode"
16724                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
16725        }));
16726    }
16727
16728    #[test]
16729    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
16730        let source = r#"
16731#ifndef TINYXML2_INCLUDED
16732#define TINYXML2_INCLUDED
16733namespace tinyxml2 {
16734class TINYXML2_LIB XMLUtil {
16735 public:
16736  static const char* SkipWhiteSpace(const char* p) {
16737    while (*p) {
16738      if (*p == ' ') {
16739        ++p;
16740      }
16741    }
16742    return p;
16743  }
16744  static bool StringEqual(const char* p, const char* q) {
16745    return p == q;
16746  }
16747  class TINYXML2_LIB Helper {
16748   public:
16749    void Touch();
16750  };
16751  static void ToStr(int value, char* buffer);
16752 private:
16753  static const char* writeBoolTrue;
16754};
16755
16756class TINYXML2_LIB XMLNode {
16757 public:
16758  virtual XMLNode* ShallowClone() const = 0;
16759  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
16760};
16761}
16762#endif
16763"#;
16764        let mut parser = tree_sitter::Parser::new();
16765        parser
16766            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16767            .unwrap();
16768        let tree = parser.parse(source, None).unwrap();
16769        let mut boundary_found = false;
16770        walk_named_tree_preorder(tree.root_node(), true, |node| {
16771            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
16772                && name == "XMLUtil"
16773            {
16774                boundary_found = fragmented_export_sibling_class_boundary(node, source)
16775                    .and_then(|boundary| {
16776                        recover_exported_class_function_definition(boundary, source)
16777                    })
16778                    .is_some_and(|(_, name, _)| name == "XMLNode");
16779            }
16780            WalkControl::Continue
16781        });
16782        assert!(
16783            boundary_found,
16784            "fixture must exercise the recovered sibling boundary"
16785        );
16786
16787        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
16788        assert!(
16789            parsed
16790                .declarations()
16791                .iter()
16792                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
16793            "{:#?}",
16794            parsed.declarations()
16795        );
16796        assert!(
16797            parsed
16798                .declarations()
16799                .iter()
16800                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
16801            "{:#?}",
16802            parsed.declarations()
16803        );
16804        assert!(parsed.declarations().iter().any(|unit| {
16805            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
16806        }));
16807        assert!(
16808            parsed
16809                .declarations()
16810                .iter()
16811                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
16812        );
16813        assert!(
16814            parsed
16815                .declarations()
16816                .iter()
16817                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
16818        );
16819    }
16820
16821    #[test]
16822    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
16823        // Clang's diagnostic suite intentionally contains this ill-formed
16824        // spelling. The analyzer must retain the parser's explicit-global AST
16825        // boundary instead of constructing `cwg311::::cwg311::X`.
16826        let parsed = parse_cpp_declarations(
16827            r#"
16828namespace cwg311 {
16829namespace X { namespace Y {} }
16830namespace ::cwg311::X {}
16831}
16832"#,
16833            "explicit-global-namespace.cpp",
16834        );
16835
16836        assert!(parsed.declarations().iter().any(|unit| {
16837            unit.kind() == CodeUnitType::Module
16838                && unit.short_name() == "cwg311::X"
16839                && unit.fq_name() == "cwg311::X"
16840        }));
16841        assert!(
16842            parsed
16843                .declarations()
16844                .iter()
16845                .all(|unit| !unit.short_name().contains("::::")),
16846            "recovered namespace names must not retain empty scope components: {:#?}",
16847            parsed.declarations()
16848        );
16849    }
16850
16851    #[test]
16852    fn repeated_scope_separator_does_not_create_empty_function_owner() {
16853        let scope = ScopeInfo {
16854            package_name: "X".to_string(),
16855            module: None,
16856            class_unit: None,
16857            template_signature: None,
16858            template_metadata: None,
16859            declarations_are_fields: false,
16860            recovered_specialization_member_scope: false,
16861            visible_using_namespaces: Vec::new(),
16862        };
16863
16864        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
16865
16866        assert!(owner.is_none());
16867        assert_eq!(name, "doit");
16868        assert_eq!(package, "X");
16869    }
16870
16871    #[test]
16872    fn trailing_decltype_expression_is_not_a_function_declarator() {
16873        let source = r#"
16874namespace boost { namespace detail {
16875#if ! defined(BOOST_NO_SFINAE_EXPR) && \
16876    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
16877    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
16878#define BOOST_THREAD_PROVIDES_INVOKE
16879#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
16880template <class Fp, class A0, class ...Args>
16881inline auto
16882invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
16883       BOOST_THREAD_RV_REF(Args) ...args)
16884    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
16885{
16886    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
16887}
16888#endif
16889#endif
16890}}
16891"#;
16892        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
16893
16894        assert!(
16895            parsed
16896                .declarations()
16897                .iter()
16898                .all(|unit| unit.short_name() != ".*f")
16899        );
16900    }
16901
16902    fn find_class_named<'tree>(
16903        root: Node<'tree>,
16904        source: &str,
16905        expected_name: &str,
16906    ) -> Option<Node<'tree>> {
16907        let mut stack = vec![root];
16908        while let Some(node) = stack.pop() {
16909            if node.kind() == "class_specifier"
16910                && node
16911                    .child_by_field_name("name")
16912                    .is_some_and(|name| node_text(name, source) == expected_name)
16913            {
16914                return Some(node);
16915            }
16916            let mut cursor = node.walk();
16917            stack.extend(node.named_children(&mut cursor));
16918        }
16919        None
16920    }
16921
16922    #[test]
16923    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
16924        let source = r#"EXPORT void definition(struct Value value) {}
16925EXPORT void prototype(struct Value value);
16926"#;
16927        let mut parser = tree_sitter::Parser::new();
16928        parser
16929            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16930            .unwrap();
16931        let tree = parser.parse(source, None).unwrap();
16932        let root = tree.root_node();
16933        let mut cursor = root.walk();
16934        let callables = root
16935            .named_children(&mut cursor)
16936            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
16937            .collect::<Vec<_>>();
16938
16939        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
16940        for callable in callables {
16941            assert!(callable.has_error(), "fixture must exercise error recovery");
16942            assert!(
16943                cpp_sentinel_macro_parts(callable, source).is_none(),
16944                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
16945            );
16946        }
16947    }
16948
16949    #[test]
16950    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
16951        let source = r#"namespace absl {
16952ABSL_NAMESPACE_BEGIN
16953// Generate a floating-point variate conforming to a Beta distribution:
16954template <typename RealType = double>
16955class beta_distribution {
16956 public:
16957  using result_type = RealType;
16958
16959
16960  beta_distribution() : beta_distribution(1) {}
16961
16962  explicit beta_distribution(result_type alpha, result_type beta = 1)
16963      : param_(alpha, beta) {}
16964
16965  explicit beta_distribution(const param_type& p) : param_(p) {}
16966
16967  void reset() {}
16968
16969  // Generating functions
16970  template <typename URBG>
16971  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
16972    return (*this)(g, param_);
16973  }
16974
16975};
16976ABSL_NAMESPACE_END
16977}  // namespace absl
16978"#;
16979        let mut parser = tree_sitter::Parser::new();
16980        parser
16981            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16982            .unwrap();
16983        let tree = parser.parse(source, None).unwrap();
16984        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
16985        let body = namespace
16986            .child_by_field_name("body")
16987            .expect("fixture namespace body");
16988        let sentinel = body.named_child(0).expect("sentinel envelope");
16989        let callable = sentinel
16990            .child_by_field_name("declarator")
16991            .and_then(extract_function_declarator)
16992            .and_then(cpp_function_declarator_name_node)
16993            .expect("preserved callable name");
16994
16995        assert_eq!(sentinel.kind(), "function_definition");
16996        assert_eq!(callable.kind(), "operator_name");
16997        assert!(
16998            cpp_sentinel_macro_parts(sentinel, source).is_some(),
16999            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
17000        );
17001    }
17002
17003    #[test]
17004    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
17005        let source = r#"namespace absl {
17006ABSL_NAMESPACE_BEGIN
17007// absl::discrete_distribution
17008//
17009// A discrete distribution produces random integers i, where 0 <= i < n
17010template <typename IntType = int>
17011class discrete_distribution {
17012 public:
17013  using result_type = IntType;
17014  class param_type {
17015   public:
17016    param_type() { init(); }
17017    template <typename InputIterator>
17018    explicit param_type(InputIterator begin, InputIterator end)
17019        : p_(begin, end) {
17020      init();
17021    }
17022  };
17023  discrete_distribution() : param_() {}
17024  explicit discrete_distribution(const param_type& p) : param_(p) {}
17025};
17026ABSL_NAMESPACE_END
17027}  // namespace absl
17028"#;
17029        let mut parser = tree_sitter::Parser::new();
17030        parser
17031            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17032            .unwrap();
17033        let tree = parser.parse(source, None).unwrap();
17034        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
17035        let body = namespace
17036            .child_by_field_name("body")
17037            .expect("fixture namespace body");
17038        let sentinel = body.named_child(0).expect("sentinel envelope");
17039        let callable = sentinel
17040            .child_by_field_name("declarator")
17041            .and_then(extract_function_declarator)
17042            .and_then(cpp_function_declarator_name_node)
17043            .expect("preserved callable name");
17044
17045        assert_eq!(sentinel.kind(), "function_definition");
17046        assert_eq!(callable.kind(), "identifier");
17047        assert!(
17048            cpp_sentinel_macro_parts(sentinel, source).is_some(),
17049            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
17050        );
17051    }
17052
17053    #[test]
17054    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
17055        let source = r#"
17056#define CPPCHECKLIB
17057class Library {
17058    struct Container {
17059        CPPCHECKLIB static std::string toString(Yield yield);
17060        CPPCHECKLIB static std::string toString(Action action);
17061    };
17062};
17063"#;
17064        let mut parser = tree_sitter::Parser::new();
17065        parser
17066            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17067            .unwrap();
17068        let tree = parser.parse(source, None).unwrap();
17069        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
17070        let parsed = parse_cpp_file(&file, source, &tree);
17071        assert!(
17072            parsed
17073                .declarations()
17074                .iter()
17075                .all(|unit| unit.fq_name() != "Library$Container.std"),
17076            "the qualified return-type namespace must not become a field: {:#?}",
17077            parsed.declarations()
17078        );
17079        for expected in ["(Yield)", "(Action)"] {
17080            assert!(
17081                parsed.declarations().iter().any(|unit| {
17082                    unit.is_function()
17083                        && unit.fq_name() == "Library$Container.toString"
17084                        && unit.signature() == Some(expected)
17085                }),
17086                "recovered toString overload {expected} is missing: {:#?}",
17087                parsed.declarations()
17088            );
17089        }
17090    }
17091
17092    #[test]
17093    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
17094        let source = r#"
17095#define SIMPLECPP_LIB
17096namespace simplecpp {
17097using TokenString = std::string;
17098struct Location { int line{}; };
17099class SIMPLECPP_LIB Token {
17100  TokenString prefix;
17101  void prefix_method() {}
17102 public:
17103  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
17104      whitespaceahead(wsahead), location(loc), string(s)
17105      // The comment must not hide the constructor body from recovery.
17106      {
17107      flags();
17108  }
17109  TokenString string;
17110  bool whitespaceahead;
17111  Location location;
17112  Token *previous{};
17113 private:
17114  void flags() {
17115      whitespaceahead = true;
17116  }
17117};
17118}
17119"#;
17120        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
17121
17122        let location_fields = parsed
17123            .declarations()
17124            .iter()
17125            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
17126            .collect::<Vec<_>>();
17127        assert_eq!(
17128            location_fields.len(),
17129            1,
17130            "location should have one class-owned declaration: {:#?}",
17131            parsed.declarations()
17132        );
17133        assert!(
17134            location_fields[0].is_field(),
17135            "location has wrong kind: {:#?}",
17136            parsed.declarations()
17137        );
17138        assert!(
17139            parsed.declarations().iter().all(|unit| {
17140                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
17141            })
17142        );
17143        assert!(
17144            parsed.declarations().iter().all(|unit| {
17145                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
17146            })
17147        );
17148        assert!(
17149            parsed
17150                .declarations()
17151                .iter()
17152                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
17153        );
17154        assert!(
17155            parsed
17156                .declarations()
17157                .iter()
17158                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
17159            "the recovered class must retain its constructor: {:#?}",
17160            parsed.declarations()
17161        );
17162        assert!(
17163            parsed
17164                .declarations()
17165                .iter()
17166                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
17167        );
17168        assert!(parsed.declarations().iter().any(|unit| {
17169            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
17170        }));
17171        let constructor = parsed
17172            .declarations()
17173            .iter()
17174            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
17175            .expect("recovered constructor");
17176        let constructor_start = source.find("Token(const").expect("constructor start");
17177        let constructor_end = source
17178            .get(
17179                ..source
17180                    .find("  TokenString string;")
17181                    .expect("constructor end"),
17182            )
17183            .expect("constructor slice")
17184            .trim_end()
17185            .len();
17186        assert!(
17187            parsed
17188                .navigation_ranges
17189                .get(constructor)
17190                .is_some_and(|ranges| {
17191                    ranges.iter().any(|range| {
17192                        range.start_byte == constructor_start && range.end_byte == constructor_end
17193                    })
17194                }),
17195            "constructor navigation must span the full body: {:#?}",
17196            parsed.navigation_ranges
17197        );
17198        assert_eq!(
17199            parsed
17200                .signature_metadata
17201                .get(constructor)
17202                .and_then(|metadata| metadata.first())
17203                .and_then(SignatureMetadata::callable_linkage),
17204            Some(CallableLinkage::External)
17205        );
17206        let token_class = parsed
17207            .declarations()
17208            .iter()
17209            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
17210            .expect("recovered Token class");
17211        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
17212        assert!(
17213            parsed
17214                .navigation_ranges
17215                .get(token_class)
17216                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
17217            "class navigation must include the terminating semicolon: {:#?}",
17218            parsed.navigation_ranges
17219        );
17220    }
17221
17222    #[test]
17223    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
17224        let source = r#"
17225#define SIMPLECPP_LIB
17226namespace simplecpp {
17227using TokenString = std::string;
17228class Macro;
17229struct Location {
17230  unsigned int fileIndex{};
17231  unsigned int line{};
17232  unsigned int col{};
17233};
17234struct Output {
17235  int type;
17236};
17237class SIMPLECPP_LIB Token {
17238 public:
17239  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
17240      whitespaceahead(wsahead), location(loc), string(s) {
17241      flags();
17242  }
17243  Token(const Token &tok) :
17244      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
17245      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
17246      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
17247  Token &operator=(const Token &tok) = delete;
17248  const TokenString& str() const { return string; }
17249  void setstr(const std::string &s) { string = s; flags(); }
17250  bool isOneOf(const char ops[]) const;
17251  TokenString macro;
17252  char op;
17253  bool comment;
17254  bool name;
17255  bool number;
17256  bool whitespaceahead;
17257  Location location;
17258  Token *previous{};
17259  Token *next{};
17260 private:
17261  void flags() {
17262      name = !string.empty();
17263      comment = false;
17264      number = false;
17265      op = 0;
17266  }
17267  TokenString string;
17268};
17269}
17270struct Following {
17271  int type;
17272};
17273class SIMPLECPP_LIB Later {
17274 public:
17275  Later(int value) : value(value) {}
17276  int value;
17277};
17278"#;
17279        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
17280        assert!(
17281            parsed
17282                .declarations()
17283                .iter()
17284                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
17285        );
17286        assert!(
17287            !parsed
17288                .declarations()
17289                .iter()
17290                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
17291        );
17292        assert!(
17293            parsed
17294                .declarations()
17295                .iter()
17296                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
17297        );
17298        assert!(
17299            !parsed
17300                .declarations()
17301                .iter()
17302                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
17303        );
17304        assert!(
17305            parsed
17306                .declarations()
17307                .iter()
17308                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
17309        );
17310        assert!(
17311            parsed
17312                .declarations()
17313                .iter()
17314                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
17315        );
17316        assert!(
17317            parsed
17318                .declarations()
17319                .iter()
17320                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
17321        );
17322        assert!(
17323            parsed
17324                .declarations()
17325                .iter()
17326                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
17327        );
17328        assert!(
17329            parsed
17330                .declarations()
17331                .iter()
17332                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
17333        );
17334        assert!(
17335            parsed
17336                .declarations()
17337                .iter()
17338                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
17339        );
17340        assert!(parsed.declarations().iter().all(|unit| {
17341            !matches!(
17342                unit.fq_name().as_str(),
17343                "simplecpp.Token.Following" | "simplecpp.Token.Later"
17344            )
17345        }));
17346        assert!(
17347            !parsed
17348                .declarations()
17349                .iter()
17350                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
17351            "the following struct must remain outside the recovered Token class"
17352        );
17353    }
17354
17355    #[test]
17356    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
17357        let source = r#"
17358#define SIMPLECPP_LIB
17359namespace {
17360namespace simplecpp {
17361using TokenString = std::string;
17362struct Location { int line{}; };
17363class SIMPLECPP_LIB HiddenToken {
17364 public:
17365  HiddenToken(const TokenString &s, const Location &loc) :
17366      location(loc), string(s) {
17367      flags();
17368  }
17369  TokenString string;
17370  Location location;
17371  HiddenToken *previous{};
17372 private:
17373  void flags() {}
17374};
17375}
17376}
17377"#;
17378        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
17379        let constructor = parsed
17380            .declarations()
17381            .iter()
17382            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
17383            .expect("recovered anonymous-namespace constructor");
17384        assert_eq!(
17385            parsed
17386                .signature_metadata
17387                .get(constructor)
17388                .and_then(|metadata| metadata.first())
17389                .and_then(SignatureMetadata::callable_linkage),
17390            Some(CallableLinkage::Internal)
17391        );
17392    }
17393
17394    #[test]
17395    fn macro_qualified_static_field_keeps_real_declarator() {
17396        let source = r#"#define JSON_INLINE_VARIABLE
17397struct Reader {
17398static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
17399static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
17400static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
17401};"#;
17402        let mut parser = tree_sitter::Parser::new();
17403        parser
17404            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17405            .unwrap();
17406        let tree = parser.parse(source, None).unwrap();
17407        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
17408        let parsed = parse_cpp_file(&file, source, &tree);
17409        for expected in [
17410            "Reader.npos",
17411            "Reader.other",
17412            "Reader.pointer",
17413            "Reader.reference",
17414        ] {
17415            assert!(
17416                parsed
17417                    .declarations()
17418                    .iter()
17419                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
17420                "real macro-decorated field {expected} is missing: {:#?}",
17421                parsed.declarations()
17422            );
17423        }
17424        assert!(
17425            parsed
17426                .declarations()
17427                .iter()
17428                .all(|unit| unit.fq_name() != "Reader.std"),
17429            "qualified type prefix became a pseudo-field: {:#?}",
17430            parsed.declarations()
17431        );
17432        let root = tree.root_node();
17433        let mut stack = vec![root];
17434        let mut signatures = Vec::new();
17435        while let Some(current) = stack.pop() {
17436            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
17437            {
17438                signatures.extend(
17439                    declarators
17440                        .into_iter()
17441                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
17442                );
17443            }
17444            let mut cursor = current.walk();
17445            stack.extend(current.named_children(&mut cursor));
17446        }
17447        signatures.sort();
17448        assert_eq!(
17449            signatures,
17450            [
17451                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
17452                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
17453                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
17454                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
17455            ]
17456        );
17457    }
17458
17459    fn member_function_linkage(source: &str) -> CallableLinkage {
17460        let mut parser = tree_sitter::Parser::new();
17461        parser
17462            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17463            .unwrap();
17464        let tree = parser.parse(source, None).unwrap();
17465        let ancestry = ParentIndex::new(tree.root_node());
17466        let mut stack = vec![tree.root_node()];
17467        while let Some(node) = stack.pop() {
17468            if node.kind() == "function_definition" {
17469                let mut current = node.parent();
17470                while let Some(parent) = current {
17471                    if matches!(
17472                        parent.kind(),
17473                        "class_specifier" | "struct_specifier" | "union_specifier"
17474                    ) {
17475                        return cpp_callable_linkage(node, source, &ancestry);
17476                    }
17477                    current = parent.parent();
17478                }
17479            }
17480            let mut cursor = node.walk();
17481            stack.extend(node.named_children(&mut cursor));
17482        }
17483        panic!("fixture has no member function definition");
17484    }
17485
17486    #[test]
17487    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
17488        assert_eq!(
17489            member_function_linkage("struct Named { int method() { return 1; } };"),
17490            CallableLinkage::External
17491        );
17492        assert_eq!(
17493            member_function_linkage(
17494                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
17495            ),
17496            CallableLinkage::Internal
17497        );
17498        assert_eq!(
17499            member_function_linkage("struct { int method() { return 1; } } instance;"),
17500            CallableLinkage::Internal
17501        );
17502        assert_eq!(
17503            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
17504            CallableLinkage::Internal
17505        );
17506    }
17507
17508    #[test]
17509    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
17510        let source = r#"
17511#ifndef PROTON_VALUE_HPP
17512#define PROTON_VALUE_HPP
17513namespace proton {
17514namespace internal {
17515class value_base {
17516  protected:
17517    internal::data& data();
17518    internal::data data_;
17519  friend class codec::encoder;
17520  friend class codec::decoder;
17521};
17522}
17523class value : public internal::value_base, private internal::comparable<value> {
17524  private:
17525    template<class T, class U=void> struct assignable :
17526        public std::enable_if<codec::is_encodable<T>::value, U> {};
17527    template<class U> struct assignable<value, U> {};
17528  public:
17529    PN_CPP_EXTERN value();
17530    PN_CPP_EXTERN value(const value&);
17531    PN_CPP_EXTERN value& operator=(const value&);
17532    PN_CPP_EXTERN value(value&&);
17533    PN_CPP_EXTERN value& operator=(value&&);
17534    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
17535    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
17536        codec::encoder e(*this);
17537        e << x;
17538        return *this;
17539    }
17540    PN_CPP_EXTERN type_id type() const;
17541    PN_CPP_EXTERN bool empty() const;
17542    PN_CPP_EXTERN void clear();
17543    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
17544    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
17545  friend PN_CPP_EXTERN void swap(value&, value&);
17546  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
17547  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
17548  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
17549    value(pn_data_t* d);
17550    void reset(pn_data_t* d = 0);
17551};
17552}
17553#endif
17554"#;
17555        let mut parser = tree_sitter::Parser::new();
17556        parser
17557            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17558            .unwrap();
17559        let tree = parser.parse(source, None).unwrap();
17560        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
17561        let parsed = parse_cpp_file(&file, source, &tree);
17562        let macro_constructors = parsed
17563            .signature_metadata
17564            .iter()
17565            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
17566            .flat_map(|(_, metadata)| metadata)
17567            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
17568            .collect::<Vec<_>>();
17569
17570        assert_eq!(
17571            macro_constructors.len(),
17572            3,
17573            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
17574            parsed.declarations()
17575        );
17576        assert!(
17577            macro_constructors.iter().all(|metadata| {
17578                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
17579            }),
17580            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
17581        );
17582    }
17583
17584    #[test]
17585    fn recovered_export_class_typedef_uses_displaced_alias_name() {
17586        let source = r#"
17587namespace spi {
17588class Filter {
17589public:
17590    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
17591};
17592}
17593namespace filter {
17594class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
17595{
17596public:
17597    typedef spi::Filter BASE_CLASS;
17598    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
17599    BEGIN_LOG4CXX_CAST_MAP()
17600    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
17601    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
17602    END_LOG4CXX_CAST_MAP()
17603    FilterDecision decide() const;
17604};
17605}
17606"#;
17607        let mut parser = tree_sitter::Parser::new();
17608        parser
17609            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17610            .unwrap();
17611        let tree = parser.parse(source, None).unwrap();
17612        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
17613        let parsed = parse_cpp_file(&file, source, &tree);
17614        assert!(
17615            parsed.declarations().iter().any(|unit| {
17616                unit.is_class()
17617                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
17618                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
17619            }),
17620            "the displaced typedef alias must retain its declared name: {:#?}",
17621            parsed.declarations()
17622        );
17623        assert!(
17624            parsed
17625                .declarations()
17626                .iter()
17627                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
17628            "the qualified underlying type must not become a false nested alias: {:#?}",
17629            parsed.declarations()
17630        );
17631    }
17632
17633    #[test]
17634    fn exported_single_base_recovery_uses_displaced_class_name() {
17635        let source = r#"
17636class CORE_EXPORT QgsPoint : public AbstractGeometry
17637{
17638    Q_GADGET
17639
17640    Q_PROPERTY( double x READ x WRITE setX )
17641    Q_PROPERTY( double y READ y WRITE setY )
17642    Q_PROPERTY( double z READ z WRITE setZ )
17643    Q_PROPERTY( double m READ m WRITE setM )
17644
17645  public:
17646#ifndef SIP_RUN
17647    QgsPoint(
17648      double x = std::numeric_limits<double>::quiet_NaN(),
17649      double y = std::numeric_limits<double>::quiet_NaN(),
17650      double z = std::numeric_limits<double>::quiet_NaN(),
17651      double m = std::numeric_limits<double>::quiet_NaN(),
17652      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
17653    );
17654#else
17655    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 )];
17656    % MethodCode
17657    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
17658    {
17659      int state;
17660      sipIsErr = 0;
17661      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
17662      if ( !sipIsErr )
17663      {
17664        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
17665      }
17666      sipReleaseType( p, sipType_QgsPointXY, state );
17667    }
17668    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
17669    {
17670      int state;
17671      sipIsErr = 0;
17672
17673      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
17674      if ( !sipIsErr )
17675      {
17676        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
17677      }
17678      sipReleaseType( p, sipType_QPointF, state );
17679    }
17680    else if (
17681      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
17682      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
17683      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
17684      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
17685    {
17686      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
17687      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
17688      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
17689      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
17690      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
17691      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
17692    }
17693    else // Invalid ctor arguments
17694    {
17695      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
17696      sipIsErr = 1;
17697    }
17698    % End
17699#endif
17700
17701    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
17702    explicit QgsPoint( QPointF p ) SIP_SKIP;
17703    explicit QgsPoint(
17704      Qgis::WkbType wkbType,
17705      double x = std::numeric_limits<double>::quiet_NaN(),
17706      double y = std::numeric_limits<double>::quiet_NaN(),
17707      double z = std::numeric_limits<double>::quiet_NaN(),
17708      double m = std::numeric_limits<double>::quiet_NaN()
17709    ) SIP_SKIP;
17710    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
17711    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
17712    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
17713#ifndef SIP_RUN
17714  private:
17715    bool fuzzyHelper(
17716      double epsilon,
17717      const AbstractGeometry &other,
17718      bool is3DFlag,
17719      bool isMeasureFlag
17720    ) const
17721    {
17722      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
17723    }
17724#endif
17725};
17726class Ordinary : public Base { public: Ordinary(); };
17727class API_EXPORT Plain { public: Plain(); };
17728class API_EXPORT : public Base {};
17729class
17730PN_CPP_CLASS_EXTERN Sender : public Link {
17731    Sender();
17732    struct impl;
17733    struct impl& get_impl() const;
17734};
17735class thread_ctx_t {};
17736class ctx_t ZMQ_FINAL : public thread_ctx_t {
17737    bool start();
17738};
17739"#;
17740        let mut parser = tree_sitter::Parser::new();
17741        parser
17742            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17743            .unwrap();
17744        let tree = parser.parse(source, None).unwrap();
17745        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
17746        let parsed = parse_cpp_file(&file, source, &tree);
17747        let declarations = parsed.declarations();
17748
17749        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
17750            assert!(
17751                declarations
17752                    .iter()
17753                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
17754                "missing recovered class {expected}: {declarations:#?}"
17755            );
17756        }
17757        let qgs_point = declarations
17758            .iter()
17759            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
17760            .expect("recovered QgsPoint class");
17761        assert_eq!(
17762            parsed.raw_supertypes.get(qgs_point),
17763            Some(&vec!["AbstractGeometry".to_string()]),
17764            "single-base export recovery must retain its displaced base"
17765        );
17766        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
17767        assert!(
17768            parsed
17769                .navigation_ranges
17770                .get(qgs_point)
17771                .is_some_and(|ranges| {
17772                    !ranges.is_empty()
17773                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
17774                }),
17775            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
17776            parsed.navigation_ranges.get(qgs_point)
17777        );
17778        let sender = declarations
17779            .iter()
17780            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
17781            .expect("recovered Sender class");
17782        assert_eq!(
17783            parsed.raw_supertypes.get(sender),
17784            Some(&vec!["Link".to_string()]),
17785            "post-declarator export recovery must retain its displaced base"
17786        );
17787        let recovered_member = declarations
17788            .iter()
17789            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
17790            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
17791        assert_eq!(
17792            parsed
17793                .signature_metadata
17794                .get(recovered_member)
17795                .and_then(|metadata| metadata.first())
17796                .and_then(SignatureMetadata::callable_linkage),
17797            Some(CallableLinkage::External),
17798            "a named recovered class's members have external linkage"
17799        );
17800        let ctx = declarations
17801            .iter()
17802            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
17803            .expect("recovered ctx_t class");
17804        assert_eq!(
17805            parsed.raw_supertypes.get(ctx),
17806            Some(&vec!["thread_ctx_t".to_string()]),
17807            "postfix export-macro recovery must retain its displaced base"
17808        );
17809        assert!(
17810            declarations.iter().any(|unit| {
17811                unit.is_function()
17812                    && unit.fq_name() == "QgsPoint.QgsPoint"
17813                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
17814            }),
17815            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
17816        );
17817        assert!(
17818            declarations.iter().all(|unit| {
17819                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
17820            }),
17821            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
17822        );
17823    }
17824
17825    #[test]
17826    fn function_like_export_macro_classes_keep_names_and_base_edges() {
17827        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
17828        // error: a plain body, a single base, `final` without a base, `final`
17829        // with one base, and `final` with a comma-separated base list.
17830        let source = r#"
17831namespace api {
17832class PROJECT_PUBLIC_API(2, 0) Prelude {
17833  public:
17834    Prelude();
17835};
17836class PROJECT_PUBLIC_API(2, 0) Base {
17837  public:
17838    Base(int value);
17839};
17840class PROJECT_PUBLIC_API(2, 0) Mixin {
17841  public:
17842    Mixin();
17843};
17844class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
17845  public:
17846    Adopted(int value);
17847};
17848class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
17849  public:
17850    Derived(int value);
17851};
17852class PROJECT_PUBLIC_API(2, 0) Solo final {
17853  public:
17854    Solo();
17855};
17856class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
17857  public:
17858    Blended(int value);
17859};
17860class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
17861  public:
17862    Woven(int value);
17863};
17864} // namespace api
17865"#;
17866        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
17867        let declarations = parsed.declarations();
17868        let class_named = |name: &str| {
17869            declarations
17870                .iter()
17871                .find(|unit| unit.is_class() && unit.fq_name() == name)
17872                .unwrap_or_else(|| {
17873                    panic!("missing function-like export macro class {name}: {declarations:#?}")
17874                })
17875        };
17876        let base = class_named("api.Base");
17877        class_named("api.Prelude");
17878        class_named("api.Mixin");
17879
17880        assert_eq!(
17881            parsed.raw_supertypes.get(class_named("api.Adopted")),
17882            Some(&vec!["Base".to_string()])
17883        );
17884        assert_eq!(
17885            parsed.raw_supertypes.get(class_named("api.Derived")),
17886            Some(&vec!["Base".to_string()])
17887        );
17888        assert_eq!(
17889            parsed.raw_supertypes.get(class_named("api.Solo")),
17890            None,
17891            "a final class without a base list must not invent a supertype"
17892        );
17893        assert_eq!(
17894            parsed.raw_supertypes.get(class_named("api.Blended")),
17895            Some(&vec!["Base".to_string(), "Mixin".to_string()])
17896        );
17897        assert_eq!(
17898            parsed.raw_supertypes.get(class_named("api.Woven")),
17899            Some(&vec!["Base".to_string(), "Mixin".to_string()])
17900        );
17901        assert!(
17902            declarations
17903                .iter()
17904                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
17905            "the export macro must not become a declaration: {declarations:#?}"
17906        );
17907        assert!(
17908            declarations.iter().all(|unit| !matches!(
17909                unit.identifier(),
17910                "final" | "public" | "protected" | "private"
17911            )),
17912            "the head specifiers must not become declarations: {declarations:#?}"
17913        );
17914        assert!(
17915            parsed
17916                .navigation_ranges
17917                .get(base)
17918                .is_some_and(|ranges| !ranges.is_empty()),
17919            "the recovered base must retain a navigable declaration range"
17920        );
17921    }
17922
17923    #[test]
17924    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
17925        // #2557: the class name is the last identifier before the head ends
17926        // (`final`, the base clause `:`, or the body), whatever its spelling.
17927        // A class named in capitals (`X509_CA`) is a class, and an object-like
17928        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
17929        // the control whose spelling never mattered.
17930        let source = r#"
17931namespace api {
17932class PROJECT_PUBLIC_API(2, 0) Base {
17933  public:
17934    Base();
17935};
17936class PROJECT_PUBLIC_API(2, 0) Mixin {
17937  public:
17938    Mixin();
17939};
17940class PROJECT_PUBLIC_API(2, 0) Name {
17941  public:
17942    Name();
17943};
17944class PROJECT_PUBLIC_API(2, 0) X509_CA final {
17945  public:
17946    X509_CA();
17947};
17948class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
17949  public:
17950    HSS_LMS_KEY();
17951};
17952class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
17953  public:
17954    GOST_3410();
17955};
17956class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
17957  public:
17958    PKCS11_RSA();
17959};
17960class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
17961  public:
17962    Plain();
17963};
17964class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
17965  public:
17966    Decorated();
17967};
17968class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
17969  public:
17970    Layered();
17971};
17972} // namespace api
17973"#;
17974        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
17975        let declarations = parsed.declarations();
17976        let class_named = |name: &str| {
17977            declarations
17978                .iter()
17979                .find(|unit| unit.is_class() && unit.fq_name() == name)
17980                .unwrap_or_else(|| {
17981                    panic!("missing function-like export macro class {name}: {declarations:#?}")
17982                })
17983        };
17984        for (name, bases) in [
17985            ("api.Name", None),
17986            ("api.X509_CA", None),
17987            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
17988            ("api.GOST_3410", Some(vec!["Base"])),
17989            ("api.PKCS11_RSA", None),
17990            ("api.Plain", None),
17991            ("api.Decorated", Some(vec!["Base"])),
17992            ("api.Layered", Some(vec!["Base", "Mixin"])),
17993        ] {
17994            let expected =
17995                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
17996            assert_eq!(
17997                parsed.raw_supertypes.get(class_named(name)),
17998                expected.as_ref(),
17999                "{name}"
18000            );
18001        }
18002        assert!(
18003            declarations.iter().all(|unit| !matches!(
18004                unit.identifier(),
18005                "PROJECT_PUBLIC_API"
18006                    | "OTHER_MACRO"
18007                    | "FIRST_MACRO"
18008                    | "SECOND_MACRO"
18009                    | "final"
18010                    | "public"
18011            )),
18012            "macros and head specifiers must not become declarations: {declarations:#?}"
18013        );
18014    }
18015
18016    #[test]
18017    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
18018        // The class embedded in a preceding malformed body follows the same
18019        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
18020        let fixture = |head: &str, name: &str| {
18021            format!(
18022                r#"
18023namespace api {{
18024class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
18025   public:
18026      /** Return a descriptive string. */
18027      const char* what() const noexcept override {{ return m_msg.c_str(); }}
18028
18029      /** Return the type of error. */
18030      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
18031
18032      /** Return an associated error code. */
18033      virtual int error_code() const noexcept {{ return 0; }}
18034
18035      /** Avoid throwing the base directly. */
18036      explicit Exception(std::string_view msg);
18037
18038      /** Avoid throwing the base directly. */
18039      Exception(const char* prefix, std::string_view msg);
18040
18041      /** Avoid throwing the base directly. */
18042      Exception(std::string_view msg, const std::exception& e);
18043
18044   private:
18045      std::string m_msg;
18046}};
18047
18048class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
18049   public:
18050      explicit {name}(std::string_view msg);
18051
18052      explicit {name}(std::string_view msg, std::string_view where);
18053
18054      {name}(std::string_view msg, const std::exception& e);
18055
18056      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
18057}};
18058}} // namespace api
18059"#
18060            )
18061        };
18062        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
18063            let source = fixture(head, name);
18064            let mut parser = Parser::new();
18065            parser
18066                .set_language(&tree_sitter_cpp::LANGUAGE.into())
18067                .expect("set C++ grammar");
18068            let tree = parser.parse(&source, None).expect("parse fixture");
18069            let mut embedded = Vec::new();
18070            let mut stack = vec![tree.root_node()];
18071            while let Some(node) = stack.pop() {
18072                embedded.extend(
18073                    recover_embedded_function_like_export_classes(node, &source)
18074                        .into_iter()
18075                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
18076                );
18077                let mut cursor = node.walk();
18078                stack.extend(node.named_children(&mut cursor));
18079            }
18080            assert!(
18081                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
18082                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
18083                tree.root_node().to_sexp()
18084            );
18085            assert!(
18086                embedded
18087                    .iter()
18088                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
18089                "{head}: the object-like macro is not a class: {embedded:#?}"
18090            );
18091
18092            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
18093            let declarations = parsed.declarations();
18094            let class = declarations
18095                .iter()
18096                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
18097                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
18098            assert_eq!(
18099                parsed.raw_supertypes.get(class),
18100                Some(&vec!["Exception".to_string()]),
18101                "{head}"
18102            );
18103            assert!(
18104                declarations
18105                    .iter()
18106                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
18107                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
18108            );
18109        }
18110    }
18111
18112    #[test]
18113    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
18114        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar swallows
18115        // the whole head into the `class MACRO(3, 6)` error, keeps the first
18116        // base as the `type` of the following declaration, and closes that
18117        // declaration with a zero-width `MISSING identifier`. Reading the head
18118        // by position must not take that missing node for the class name: an
18119        // empty name panics at `FqName` construction, which took the Botan
18120        // corpus replay down (#2557). The class itself stays unrecovered here,
18121        // and the bodyless `class PROJECT_PUBLIC_API` specifier still surfaces
18122        // the way it did before this change.
18123        let source = r#"
18124namespace api {
18125class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
18126                                                    public virtual Botan::EC_PublicKey {
18127   public:
18128      std::string algo_name() const override { return "ECDSA"; }
18129};
18130} // namespace api
18131"#;
18132        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
18133        let declarations = parsed.declarations();
18134        assert!(
18135            declarations
18136                .iter()
18137                .all(|unit| !unit.identifier().is_empty()),
18138            "no declaration may carry an empty name: {declarations:#?}"
18139        );
18140        assert!(
18141            declarations
18142                .iter()
18143                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
18144            "macros and head specifiers must not become declarations: {declarations:#?}"
18145        );
18146    }
18147
18148    #[test]
18149    fn function_like_export_class_survives_a_preceding_malformed_body() {
18150        let source = r#"
18151namespace api {
18152class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
18153   public:
18154      /** Return a descriptive string. */
18155      const char* what() const noexcept override { return m_msg.c_str(); }
18156
18157      /** Return the type of error. */
18158      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
18159
18160      /** Return an associated error code. */
18161      virtual int error_code() const noexcept { return 0; }
18162
18163      /** Avoid throwing the base directly. */
18164      explicit Exception(std::string_view msg);
18165
18166      /** Avoid throwing the base directly. */
18167      Exception(const char* prefix, std::string_view msg);
18168
18169      /** Avoid throwing the base directly. */
18170      Exception(std::string_view msg, const std::exception& e);
18171
18172   private:
18173      std::string m_msg;
18174};
18175
18176class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
18177   public:
18178      explicit Invalid_Argument(std::string_view msg);
18179
18180      explicit Invalid_Argument(std::string_view msg, std::string_view where);
18181
18182      Invalid_Argument(std::string_view msg, const std::exception& e);
18183
18184      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
18185};
18186} // namespace api
18187"#;
18188        let mut parser = Parser::new();
18189        parser
18190            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18191            .expect("set C++ grammar");
18192        let tree = parser.parse(source, None).expect("parse fixture");
18193        let mut stack = vec![tree.root_node()];
18194        let mut saw_embedded_shape = false;
18195        while let Some(node) = stack.pop() {
18196            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
18197                .iter()
18198                .any(|recovered| recovered.name == "Invalid_Argument");
18199            let mut cursor = node.walk();
18200            stack.extend(node.named_children(&mut cursor));
18201        }
18202        assert!(
18203            saw_embedded_shape,
18204            "fixture must retain the embedded error geometry: {}",
18205            tree.root_node().to_sexp()
18206        );
18207
18208        let parsed = parse_cpp_file(
18209            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
18210            source,
18211            &tree,
18212        );
18213        let declarations = parsed.declarations();
18214        let exception = declarations
18215            .iter()
18216            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
18217            .expect("qualified-base export class");
18218        let invalid = declarations
18219            .iter()
18220            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
18221            .expect("class embedded in the preceding malformed body");
18222
18223        assert_eq!(
18224            parsed.raw_supertypes.get(exception),
18225            Some(&vec!["std::exception".to_string()])
18226        );
18227        assert_eq!(
18228            parsed.raw_supertypes.get(invalid),
18229            Some(&vec!["Exception".to_string()])
18230        );
18231        assert!(
18232            parsed.materialization_records.iter().any(|record| matches!(
18233                record,
18234                MaterializationRecord::RecoveredDeclaration { unit, .. }
18235                    if unit == invalid
18236            )),
18237            "the embedded class must retain recovery provenance: {:#?}",
18238            parsed.materialization_records
18239        );
18240    }
18241
18242    #[test]
18243    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
18244        let source = r#"
18245public:
18246   explicit Lookup_Error(std::string_view err) : Exception(err) {}
18247
18248   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
18249"#;
18250        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
18251            .expect("reparse merged constructor body");
18252        let (range, body) =
18253            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
18254                .unwrap_or_else(|| {
18255                    panic!(
18256                        "the merged constructor must retain its structured declarator/body: {}",
18257                        tree.root_node().to_sexp()
18258                    )
18259                });
18260        assert_eq!(
18261            source.get(range).expect("constructor range"),
18262            "Lookup_Error(std::string_view err) : Exception(err) {}"
18263        );
18264        assert_eq!(node_text(body, source), "{}");
18265    }
18266
18267    #[test]
18268    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
18269        let positive_source =
18270            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
18271        let mut parser = tree_sitter::Parser::new();
18272        parser
18273            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18274            .unwrap();
18275        let positive_tree = parser.parse(positive_source, None).unwrap();
18276        assert!(cpp_reparsed_members_are_indexable(
18277            positive_tree.root_node(),
18278            positive_source
18279        ));
18280
18281        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
18282        let negative_tree = parser.parse(negative_source, None).unwrap();
18283        assert!(!cpp_reparsed_members_are_indexable(
18284            negative_tree.root_node(),
18285            negative_source
18286        ));
18287    }
18288
18289    #[test]
18290    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
18291        let copy_control_source = r#"
18292public:
18293    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
18294    explicit Token(const Token* tok);
18295    ~Token();
18296    Token* astOperand1() { return nullptr; }
18297"#;
18298        let constraint_source = r#"
18299private:
18300    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
18301    static T *tokAtImpl(T *tok, int index) {
18302        return tok;
18303    }
18304
18305    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
18306    static T *linkAtImpl(T *tok, int index) {
18307        return tok;
18308    }
18309
18310public:
18311    int late() const { return 1; }
18312"#;
18313        let mut parser = tree_sitter::Parser::new();
18314        parser
18315            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18316            .unwrap();
18317        let copy_control_tree = parser
18318            .parse(copy_control_source, None)
18319            .expect("parse copy-control fixture");
18320        assert!(
18321            copy_control_tree.root_node().has_error(),
18322            "fixture must exercise adjacent copy-control recovery"
18323        );
18324        assert!(
18325            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
18326            "a complete late getter must remain recoverable after adjacent copy-control declarations"
18327        );
18328        let mut cursor = copy_control_tree.root_node().walk();
18329        assert!(
18330            copy_control_tree
18331                .root_node()
18332                .named_children(&mut cursor)
18333                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
18334            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
18335            copy_control_tree.root_node().to_sexp()
18336        );
18337        let constraint_tree = parser
18338            .parse(constraint_source, None)
18339            .expect("parse constraint-macro fixture");
18340        assert!(constraint_tree.root_node().has_error());
18341        assert!(
18342            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
18343            "complete constraint-macro members must not hide a later ordinary member"
18344        );
18345        let mut cursor = constraint_tree.root_node().walk();
18346        assert!(
18347            constraint_tree
18348                .root_node()
18349                .named_children(&mut cursor)
18350                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
18351                    child,
18352                    constraint_source
18353                )),
18354            "fixture must retain the split constraint-macro prefix/function geometry"
18355        );
18356    }
18357
18358    #[test]
18359    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
18360        let source = r#"
18361struct Analyzer {
18362    struct Action {
18363        Action() = default;
18364        Action(const Action&) = default;
18365        Action& operator=(const Action& rhs) & = default;
18366
18367        template<class T,
18368                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
18369                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
18370        // NOLINTNEXTLINE(google-explicit-constructor)
18371        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
18372        {}
18373
18374        enum : std::uint16_t { None = 0, Read = (1 << 0) };
18375        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
18376
18377    private:
18378        unsigned int mFlag{};
18379    };
18380
18381    enum class Direction : unsigned char { Forward, Reverse };
18382    virtual Action analyze(Direction d) const = 0;
18383};
18384"#;
18385        let mut parser = tree_sitter::Parser::new();
18386        parser
18387            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18388            .unwrap();
18389        let tree = parser.parse(source, None).unwrap();
18390        assert!(tree.root_node().has_error());
18391        let root = tree.root_node();
18392        let outer = root
18393            .named_children(&mut root.walk())
18394            .find(|child| child.kind() == "ERROR")
18395            .expect("fragmented Analyzer prefix");
18396        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
18397            .expect("structured Analyzer fragment boundary");
18398        assert_eq!(outer_name, "Analyzer");
18399        let outer_tree = cpp_reparse_fragmented_class_body(
18400            source,
18401            outer_fragment.reparse_start,
18402            outer_fragment.reparse_end,
18403        )
18404        .expect("reparse Analyzer body");
18405        let outer_root = outer_tree.root_node();
18406        let action_prefix = outer_root
18407            .named_children(&mut outer_root.walk())
18408            .find(|child| child.kind() == "ERROR")
18409            .expect("fragmented Action prefix");
18410        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
18411            .expect("structured Action fragment boundary");
18412        assert_eq!(action_name, "Action");
18413        let action_tree = cpp_reparse_fragmented_class_body(
18414            source,
18415            action_fragment.reparse_start,
18416            action_fragment.reparse_end,
18417        )
18418        .expect("reparse Action body");
18419        let action_root = action_tree.root_node();
18420        let macro_prefix = action_root
18421            .named_children(&mut action_root.walk())
18422            .find(|child| child.kind() == "ERROR")
18423            .expect("constraint macro prefix");
18424        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
18425            .expect("structured template macro prefix");
18426        let macro_companion =
18427            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
18428        assert!(
18429            cpp_reparsed_template_macro_constructor_companion_is_indexable(
18430                macro_companion,
18431                macro_parameter,
18432                source,
18433            ),
18434            "split constrained constructor must be admitted: {}",
18435            macro_companion.to_sexp()
18436        );
18437        assert!(
18438            cpp_reparsed_members_are_indexable(action_root, source),
18439            "complete Action body must pass the recovery gate: {}",
18440            action_tree.root_node().to_sexp()
18441        );
18442        assert!(
18443            cpp_reparsed_members_are_indexable(outer_root, source),
18444            "complete Analyzer body must pass the recovery gate: {}",
18445            outer_tree.root_node().to_sexp()
18446        );
18447        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
18448        let parsed = parse_cpp_file(&file, source, &tree);
18449        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
18450            assert!(
18451                parsed
18452                    .declarations()
18453                    .iter()
18454                    .any(|unit| unit.fq_name() == expected),
18455                "missing recovered declaration {expected}: {:#?}",
18456                parsed.declarations()
18457            );
18458        }
18459        assert!(
18460            parsed
18461                .declarations()
18462                .iter()
18463                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
18464            "nested members must not remain flattened: {:#?}",
18465            parsed.declarations()
18466        );
18467    }
18468
18469    #[test]
18470    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
18471        let source = r#"
18472raw_hash_set& operator=(raw_hash_set&& that) {
18473  return move_assign(
18474      std::move(that),
18475      typename AllocTraits::propagate_on_container_move_assignment());
18476}
18477
18478iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
18479  return {};
18480}
18481
18482void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
18483
18484iterator insert(const_iterator hint, value_type&& value)
18485    ABSL_ATTRIBUTE_LIFETIME_BOUND {
18486  return {};
18487}
18488
18489friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
18490  return left.size() == right.size();
18491}
18492
18493static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
18494  return static_cast<slot_type*>(buffer);
18495}
18496
18497protected:
18498// Included-range recovery can attach this comment to the template prefix.
18499template <class K>
18500void AssertOnFind([[maybe_unused]] const K& key) {
18501  Check(key);
18502}
18503"#;
18504        let mut parser = tree_sitter::Parser::new();
18505        parser
18506            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18507            .unwrap();
18508        let tree = parser.parse(source, None).unwrap();
18509        assert!(
18510            tree.root_node().has_error(),
18511            "the fixture must exercise tree-sitter's errorful member shapes"
18512        );
18513        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
18514
18515        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
18516        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
18517        assert!(!cpp_reparsed_members_are_indexable(
18518            incomplete_tree.root_node(),
18519            incomplete_source
18520        ));
18521
18522        let outside_error_source = "int foo() stray_attribute {}\n";
18523        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
18524        assert!(outside_error_tree.root_node().has_error());
18525        assert!(!cpp_reparsed_members_are_indexable(
18526            outside_error_tree.root_node(),
18527            outside_error_source
18528        ));
18529
18530        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
18531        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
18532        assert!(!cpp_reparsed_members_are_indexable(
18533            variable_initializer_tree.root_node(),
18534            variable_initializer_source
18535        ));
18536    }
18537
18538    #[test]
18539    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
18540        let positive_source = r#"
18541std::pair<iterator, bool> insert(init_type&& value)
18542    ABSL_ATTRIBUTE_LIFETIME_BOUND
18543#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
18544  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
18545#endif
18546{
18547  return emplace(std::move(value));
18548}
18549"#;
18550        let mut parser = tree_sitter::Parser::new();
18551        parser
18552            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18553            .unwrap();
18554        let positive_tree = parser.parse(positive_source, None).unwrap();
18555        assert!(
18556            positive_tree.root_node().has_error(),
18557            "the fixture must exercise the split attribute/requires shape"
18558        );
18559        assert!(cpp_reparsed_members_are_indexable(
18560            positive_tree.root_node(),
18561            positive_source
18562        ));
18563
18564        let template_return_source = r#"
18565pair<int> insert(init_type&& value)
18566    ABSL_ATTRIBUTE_LIFETIME_BOUND
18567#if LANGUAGE_LEVEL >= 202002L
18568  requires(!Predicate<init_type>::value)
18569#endif
18570// Attributes and the function body may be separated by comments.
18571{
18572  return {};
18573}
18574"#;
18575        let template_return_tree = parser.parse(template_return_source, None).unwrap();
18576        assert!(
18577            cpp_reparsed_members_are_indexable(
18578                template_return_tree.root_node(),
18579                template_return_source
18580            ),
18581            "template-return attribute/requires tree: {}",
18582            template_return_tree.root_node().to_sexp()
18583        );
18584
18585        let no_body_source = r#"
18586std::pair<iterator, bool> insert(init_type&& value)
18587    ABSL_ATTRIBUTE_LIFETIME_BOUND
18588#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
18589  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
18590#endif
18591+ 0;
18592"#;
18593        let no_body_tree = parser.parse(no_body_source, None).unwrap();
18594        assert!(!cpp_reparsed_members_are_indexable(
18595            no_body_tree.root_node(),
18596            no_body_source
18597        ));
18598
18599        let extra_payload_source = r#"
18600pair<int> insert(init_type&& value)
18601    ABSL_ATTRIBUTE_LIFETIME_BOUND
18602#if LANGUAGE_LEVEL >= 202002L
18603  int unrelated;
18604  requires(Predicate<init_type>::value)
18605#endif
18606{
18607  return {};
18608}
18609"#;
18610        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
18611        assert!(!cpp_reparsed_members_are_indexable(
18612            extra_payload_tree.root_node(),
18613            extra_payload_source
18614        ));
18615
18616        let variable_initializer_source = r#"
18617int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
18618#if LANGUAGE_LEVEL >= 202002L
18619  requires(true)
18620#endif
18621{
18622  bad;
18623}
18624"#;
18625        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
18626        assert!(!cpp_reparsed_members_are_indexable(
18627            variable_initializer_tree.root_node(),
18628            variable_initializer_source
18629        ));
18630    }
18631
18632    #[test]
18633    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
18634        let source = r#"namespace absl {
18635ABSL_NAMESPACE_BEGIN namespace container_internal {
18636
18637class raw_hash_set : public Base {
18638 public:
18639  using value_type = int;
18640
18641  template <class U,
18642            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
18643  void insert(U value) { (void)value; }
18644
18645  struct InsertSlot {
18646    raw_hash_set& s;
18647  };
18648};
18649
18650}
18651ABSL_NAMESPACE_END
18652}"#;
18653        let mut parser = tree_sitter::Parser::new();
18654        parser
18655            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18656            .unwrap();
18657        let tree = parser.parse(source, None).unwrap();
18658        let root = tree.root_node();
18659        let outer_namespace = root
18660            .named_children(&mut root.walk())
18661            .find(|child| child.kind() == "namespace_definition")
18662            .expect("outer absl namespace");
18663        let declaration_list = outer_namespace
18664            .child_by_field_name("body")
18665            .expect("outer namespace body");
18666        let sentinel_function = declaration_list
18667            .named_children(&mut declaration_list.walk())
18668            .find(|child| child.kind() == "function_definition")
18669            .expect("malformed namespace sentinel function");
18670        let ancestry = ParentIndex::new(root);
18671        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
18672            .expect("structured nested namespace sentinel");
18673        let fragmented =
18674            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
18675                .expect("fragmented raw_hash_set class");
18676        assert_eq!(fragmented.class_node.kind(), "ERROR");
18677        assert_eq!(fragmented.name, "raw_hash_set");
18678        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
18679
18680        let outer_scope =
18681            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
18682        let mut outer_siblings = Vec::new();
18683        push_cpp_sentinel_sibling_classes(
18684            &mut outer_siblings,
18685            declaration_list,
18686            sentinel.function,
18687            &outer_scope,
18688            source,
18689            &ancestry,
18690        );
18691        let [outer_shadow] = outer_siblings.as_slice() else {
18692            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
18693        };
18694        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
18695        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
18696
18697        let field = "    raw_hash_set& s;";
18698        let start = source.find(field).expect("InsertSlot field") + 4;
18699        let node = root
18700            .descendant_for_byte_range(start, start + "raw_hash_set".len())
18701            .expect("raw_hash_set type node");
18702        let recovered = cpp_sentinel_recovered_classes(root, source);
18703        let [deep_class] = recovered.as_slice() else {
18704            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
18705        };
18706        assert_eq!(
18707            deep_class.namespace_scope_components,
18708            vec!["absl", "container_internal"]
18709        );
18710        assert_eq!(
18711            deep_class.scope_components,
18712            vec!["absl", "container_internal", "raw_hash_set"]
18713        );
18714        assert!(
18715            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
18716                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
18717        );
18718
18719        assert_eq!(
18720            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
18721            Some(vec![
18722                "absl".to_string(),
18723                "container_internal".to_string(),
18724                "raw_hash_set".to_string(),
18725                "InsertSlot".to_string(),
18726            ])
18727        );
18728
18729        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
18730        let parsed = parse_cpp_file(&file, source, &tree);
18731        let raw_hash_set = parsed
18732            .declarations()
18733            .iter()
18734            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
18735            .expect("recovered raw_hash_set class");
18736        assert_eq!(
18737            raw_hash_set.fq_name(),
18738            "absl::container_internal.raw_hash_set",
18739            "the recovered declaration must publish under the deeper sentinel namespace"
18740        );
18741        assert_eq!(
18742            parsed.raw_supertypes.get(raw_hash_set),
18743            Some(&vec!["Base".to_string()]),
18744            "the structured base clause on the fragmented ERROR prefix must survive publication"
18745        );
18746        assert!(
18747            parsed.materialization_records.iter().any(|record| matches!(
18748                record,
18749                MaterializationRecord::RecoveredDeclaration { recovery, unit }
18750                    if unit == raw_hash_set && *recovery == deep_class.class_range
18751            )),
18752            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
18753            parsed.materialization_records
18754        );
18755    }
18756
18757    /// Issue #2358: recording an aggregate definition must not walk the whole
18758    /// file.
18759    ///
18760    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
18761    /// class-like shape that has a body, so the removal step runs once per
18762    /// aggregate. It used to `retain` over `top_level_declarations` and over
18763    /// *every* child list in the file on each of those calls, comparing whole
18764    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
18765    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
18766    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
18767    /// tens of thousands of times over and the C forward differential never
18768    /// finished.
18769    ///
18770    /// A definition the file has not already declared removes nothing, so the
18771    /// honest cost is zero regardless of how many other aggregates surround it.
18772    /// Two sizes an order of magnitude apart pin that the count is not merely
18773    /// small but independent of the file.
18774    ///
18775    /// The declaration walk answers every ancestor question from a
18776    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
18777    /// the root for each one (#2361). Substituting the index is only safe
18778    /// because it answers the identical question, so pin that on the shapes
18779    /// this file's recovery paths care about: anonymous and named aggregates,
18780    /// nested namespaces, templates, macro-displaced declarations and the
18781    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
18782    /// too -- `Node::parent` walks the visible tree, not the named one.
18783    #[test]
18784    fn the_parent_index_answers_what_tree_sitter_answers() {
18785        const SHAPES: [&str; 5] = [
18786            "namespace outer { namespace inner { struct Tag { int field; }; } }",
18787            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
18788            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
18789            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
18790            "class API Broken : public First, public Second {\n  void member();\n",
18791        ];
18792        for source in SHAPES {
18793            let mut parser = tree_sitter::Parser::new();
18794            parser
18795                .set_language(&tree_sitter_cpp::LANGUAGE.into())
18796                .unwrap();
18797            let tree = parser.parse(source, None).unwrap();
18798            let root = tree.root_node();
18799            let ancestry = ParentIndex::new(root);
18800            let mut nodes = 0usize;
18801            let mut stack = vec![root];
18802            while let Some(node) = stack.pop() {
18803                nodes += 1;
18804                assert_eq!(
18805                    node.parent().map(|parent| parent.id()),
18806                    ancestry.parent(node).map(|parent| parent.id()),
18807                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
18808                );
18809                let mut cursor = node.walk();
18810                stack.extend(node.children(&mut cursor));
18811            }
18812            assert!(nodes > 1, "{source:?} produced no tree to compare");
18813        }
18814    }
18815
18816    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
18817    /// index for every ancestor edge. Asking tree-sitter directly makes each
18818    /// edge re-descend from the root, turning declaration extraction on a
18819    /// deeply nested generated header from quadratic output work into a cubic
18820    /// tree walk. Exact query counts pin the route without a machine-dependent
18821    /// wall-clock ceiling.
18822    #[test]
18823    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
18824        const DEPTH: usize = 64;
18825        let mut source = String::new();
18826        for level in 0..DEPTH {
18827            writeln!(source, "namespace n{level} {{").unwrap();
18828        }
18829        source.push_str("int deepest(int value);\n");
18830        for _ in 0..DEPTH {
18831            source.push_str("}\n");
18832        }
18833
18834        let mut parser = tree_sitter::Parser::new();
18835        parser
18836            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18837            .unwrap();
18838        let tree = parser.parse(&source, None).unwrap();
18839        let root = tree.root_node();
18840        let ancestry = ParentIndex::new(root);
18841        let mut function_declarator = None;
18842        walk_named_tree_preorder(root, true, |node| {
18843            if node.kind() == "function_declarator" {
18844                function_declarator = Some(node);
18845                WalkControl::Break
18846            } else {
18847                WalkControl::Continue
18848            }
18849        });
18850        let function_declarator = function_declarator.expect("deepest function declarator");
18851        let ancestor_count =
18852            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
18853
18854        ancestry.reset_parent_query_count_for_test();
18855        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
18856        assert_eq!(DEPTH, lexical_scope.len());
18857        assert_eq!(
18858            ancestor_count + 1,
18859            ancestry.parent_query_count_for_test(),
18860            "lexical-scope ancestry bypassed the parent index"
18861        );
18862
18863        ancestry.reset_parent_query_count_for_test();
18864        assert_eq!(
18865            DispatchExtensibility::Closed,
18866            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
18867        );
18868        assert_eq!(
18869            ancestor_count,
18870            ancestry.parent_query_count_for_test(),
18871            "dispatch ancestry bypassed the parent index"
18872        );
18873
18874        ancestry.reset_parent_query_count_for_test();
18875        assert_eq!(
18876            CallableLinkage::External,
18877            cpp_callable_linkage(function_declarator, &source, &ancestry)
18878        );
18879        assert_eq!(
18880            ancestor_count + 1,
18881            ancestry.parent_query_count_for_test(),
18882            "linkage ancestry bypassed the parent index"
18883        );
18884
18885        ancestry.reset_parent_query_count_for_test();
18886        assert!(!cpp_callable_is_structural_constructor(
18887            function_declarator,
18888            &source,
18889            &ancestry
18890        ));
18891        assert_eq!(
18892            ancestor_count + 1,
18893            ancestry.parent_query_count_for_test(),
18894            "constructor ancestry bypassed the parent index"
18895        );
18896    }
18897
18898    /// Forward declarations followed by definitions are compacted as one
18899    /// batch, without rescanning the shared namespace/top-level lists for each
18900    /// tag. Definitions are intentionally visited in reverse order so the
18901    /// assertion also pins eager remove-and-reappend ordering.
18902    #[test]
18903    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
18904        for aggregates in [64usize, 512] {
18905            let mut source =
18906                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
18907            for index in 0..aggregates {
18908                writeln!(source, "struct tag{index};").unwrap();
18909            }
18910            for index in (0..aggregates).rev() {
18911                writeln!(
18912                    source,
18913                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
18914                )
18915                .unwrap();
18916            }
18917            source.push_str("}\n");
18918
18919            start_code_unit_removal_scan_probe();
18920            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
18921            let scanned = finish_code_unit_removal_scan_probe();
18922
18923            let expected_names: Vec<String> = (0..aggregates)
18924                .rev()
18925                .map(|index| format!("tag{index}"))
18926                .collect();
18927            let top_level_names: Vec<String> = parsed
18928                .top_level_declarations
18929                .iter()
18930                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
18931                .map(|unit| unit.short_name().to_string())
18932                .collect();
18933            let namespace = parsed
18934                .declarations()
18935                .iter()
18936                .find(|unit| {
18937                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
18938                })
18939                .expect("generated namespace should be declared");
18940            let child_names: Vec<String> = parsed.children[namespace]
18941                .iter()
18942                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
18943                .map(|unit| unit.short_name().to_string())
18944                .collect();
18945            assert_eq!(
18946                aggregates,
18947                parsed
18948                    .declarations()
18949                    .iter()
18950                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
18951                    .count(),
18952                "every aggregate must still be declared at {aggregates} aggregates"
18953            );
18954            assert_eq!(expected_names, top_level_names);
18955            assert_eq!(expected_names, child_names);
18956            assert_eq!(
18957                0, scanned,
18958                "replacing {aggregates} forward declarations must compact their shared lists once"
18959            );
18960        }
18961    }
18962
18963    #[test]
18964    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
18965        const DISTINCT_PER_KIND: usize = 64;
18966        let mut source = String::new();
18967        for index in 0..DISTINCT_PER_KIND {
18968            writeln!(source, "typedef int Alias{index};").unwrap();
18969        }
18970        writeln!(source, "typedef long Alias0;").unwrap();
18971        for index in 0..DISTINCT_PER_KIND {
18972            writeln!(source, "#define MACRO_{index} {index}").unwrap();
18973        }
18974        writeln!(source, "#define MACRO_0 duplicate").unwrap();
18975        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
18976
18977        let mut parser = tree_sitter::Parser::new();
18978        parser
18979            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18980            .unwrap();
18981        let tree = parser.parse(&source, None).unwrap();
18982        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
18983
18984        start_declaration_identity_comparison_probe();
18985        let parsed = parse_cpp_file(&file, &source, &tree);
18986        let comparisons = finish_declaration_identity_comparison_probe();
18987
18988        assert_eq!(
18989            DISTINCT_PER_KIND + 1,
18990            parsed
18991                .declarations()
18992                .iter()
18993                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
18994                .count(),
18995            "every physical typedef alias declaration must be retained so \
18996             conditional branch guards stay available to the resolver"
18997        );
18998        assert_eq!(
18999            DISTINCT_PER_KIND + 1,
19000            parsed
19001                .declarations()
19002                .iter()
19003                .filter(|unit| {
19004                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
19005                })
19006                .count(),
19007            "distinct macro redefinitions must remain available to temporal lookup"
19008        );
19009        assert_eq!(
19010            2,
19011            parsed
19012                .declarations()
19013                .iter()
19014                .filter(|unit| {
19015                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
19016                })
19017                .count(),
19018            "function overloads must remain distinct"
19019        );
19020
19021        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
19022        assert!(
19023            comparisons <= dedup_inputs * 4,
19024            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
19025        );
19026    }
19027
19028    #[test]
19029    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
19030        let source = r#"namespace absl {
19031ABSL_NAMESPACE_BEGIN namespace container_internal {
19032template <typename T>
19033class broken {
19034 public:
19035  using value_type = T;
19036  T operator->() const { return &operator*(); }
19037  using alias = value_type;
19038};
19039}
19040}
19041"#;
19042        let mut parser = tree_sitter::Parser::new();
19043        parser
19044            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19045            .unwrap();
19046        let tree = parser.parse(source, None).unwrap();
19047        let broken = find_class_named(tree.root_node(), source, "broken")
19048            .expect("the positive fixture must expose the broken class node");
19049        assert!(
19050            broken.has_error(),
19051            "the positive fixture must retain an internal parser error"
19052        );
19053        assert!(
19054            cpp_complete_class_body_close(broken).is_some(),
19055            "the positive fixture must expose a real class body close"
19056        );
19057        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19058        assert!(
19059            recovered.iter().any(|class| {
19060                class.scope_components == ["absl", "container_internal", "broken"]
19061            }),
19062            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
19063        );
19064    }
19065
19066    #[test]
19067    fn sentinel_recovery_keeps_members_after_nested_body_close() {
19068        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
19069NLOHMANN_BASIC_JSON_TPL_DECLARATION
19070class basic_json {
19071 private:
19072  union storage {
19073    int value;
19074  } data;
19075 public:
19076  using late_alias = int;
19077  late_alias value() const;
19078};
19079NLOHMANN_JSON_NAMESPACE_END
19080"#;
19081        let mut parser = tree_sitter::Parser::new();
19082        parser
19083            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19084            .unwrap();
19085        let tree = parser.parse(source, None).unwrap();
19086        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19087        let basic_json = recovered
19088            .iter()
19089            .find(|class| {
19090                class
19091                    .scope_components
19092                    .last()
19093                    .is_some_and(|name| name == "basic_json")
19094            })
19095            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
19096        let late_alias = source
19097            .find("late_alias value")
19098            .expect("late alias reference");
19099        assert!(
19100            basic_json.class_range.start_byte < late_alias
19101                && late_alias < basic_json.class_range.end_byte,
19102            "the recovered class range must include members after a nested close: {basic_json:#?}"
19103        );
19104    }
19105
19106    #[test]
19107    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
19108        let source = r#"namespace absl {
19109ABSL_NAMESPACE_BEGIN namespace container_internal {
19110template <typename T>
19111class broken {
19112 public:
19113  using value_type = T;
19114  T operator->() const { return &operator*(); }
19115}
19116}
19117"#;
19118        let mut parser = tree_sitter::Parser::new();
19119        parser
19120            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19121            .unwrap();
19122        let tree = parser.parse(source, None).unwrap();
19123        let broken = find_class_named(tree.root_node(), source, "broken")
19124            .expect("the negative fixture must expose the malformed class node");
19125        assert!(
19126            broken.has_error(),
19127            "the negative fixture must retain a parser error"
19128        );
19129        assert!(
19130            cpp_complete_class_body_close(broken).is_none(),
19131            "the malformed class must not expose a real body close"
19132        );
19133        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19134        assert!(
19135            recovered
19136                .iter()
19137                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
19138            "an incomplete class must not borrow the namespace close: {recovered:#?}"
19139        );
19140    }
19141
19142    #[test]
19143    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
19144        let source = r#"namespace absl {
19145ABSL_NAMESPACE_BEGIN namespace container_internal {
19146template <typename T>
19147struct broken {
19148  using value_type = T;
19149};
19150}
19151
19152#ifdef OWNER_DEF
19153template <typename T>
19154typename broken<T>::value_type broken<T>::method() {
19155  value_type value{};
19156  return value;
19157}
19158#endif
19159
19160namespace sibling {
19161template <typename T>
19162typename broken<T>::value_type broken<T>::other() {
19163  value_type value{};
19164  return value;
19165}
19166}
19167
19168ABSL_NAMESPACE_END
19169}
19170"#;
19171        let mut parser = tree_sitter::Parser::new();
19172        parser
19173            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19174            .unwrap();
19175        let tree = parser.parse(source, None).unwrap();
19176        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19177        let broken = recovered
19178            .iter()
19179            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
19180            .expect("the sentinel class must be recovered");
19181        let method_start = source
19182            .find("typename broken<T>::value_type broken<T>::method()")
19183            .expect("guarded sibling owner");
19184        let method_end = source[method_start..]
19185            .find("\n}")
19186            .map(|offset| method_start + offset + 2)
19187            .expect("guarded sibling owner close");
19188        assert!(
19189            broken
19190                .owner_ranges
19191                .iter()
19192                .any(|owner| owner.range.start_byte <= method_start
19193                    && method_end <= owner.range.end_byte),
19194            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
19195        );
19196        let sibling_start = source
19197            .find("typename broken<T>::value_type broken<T>::other()")
19198            .expect("nested namespace sibling owner");
19199        assert!(
19200            broken
19201                .owner_ranges
19202                .iter()
19203                .all(|owner| owner.range.start_byte > sibling_start
19204                    || owner.range.end_byte <= sibling_start),
19205            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
19206        );
19207    }
19208
19209    #[test]
19210    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
19211        let source = r#"#ifdef OUTER
19212namespace absl {
19213ABSL_NAMESPACE_BEGIN namespace container_internal {
19214template <typename T>
19215struct broken {
19216  using value_type = T;
19217};
19218}
19219}
19220
19221#ifdef OWNER_DEF
19222template <typename T>
19223typename broken<T>::value_type broken<T>::method() {
19224  value_type value{};
19225  return value;
19226}
19227#endif
19228#endif
19229"#;
19230        let mut parser = tree_sitter::Parser::new();
19231        parser
19232            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19233            .unwrap();
19234        let tree = parser.parse(source, None).unwrap();
19235        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
19236        let broken = recovered
19237            .iter()
19238            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
19239            .expect("the sentinel class must be recovered");
19240        let method_start = source
19241            .find("typename broken<T>::value_type broken<T>::method()")
19242            .expect("outer sibling owner");
19243        assert!(
19244            broken
19245                .owner_ranges
19246                .iter()
19247                .all(|owner| owner.range.start_byte > method_start
19248                    || owner.range.end_byte <= method_start),
19249            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
19250        );
19251    }
19252
19253    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
19254    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
19255        let mut signatures = parsed
19256            .declarations()
19257            .iter()
19258            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
19259            .filter_map(|unit| unit.signature().map(str::to_string))
19260            .collect::<Vec<_>>();
19261        signatures.sort();
19262        signatures.dedup();
19263        signatures
19264    }
19265
19266    #[test]
19267    fn callable_parameter_types_come_from_the_ast_parameter_list() {
19268        let source = r#"
19269template <typename T, ENABLE_BYTES(T)>
19270Vec256<T> DupOdd(Vec256<T> value) { return value; }
19271
19272struct Visitor {
19273  void fail(this auto const& self) {}
19274};
19275"#;
19276        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
19277        let dup_odd = parsed
19278            .declarations()
19279            .iter()
19280            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
19281            .expect("DupOdd declaration");
19282        assert_eq!(
19283            dup_odd.signature(),
19284            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
19285        );
19286        assert_eq!(
19287            parsed
19288                .signature_metadata
19289                .get(dup_odd)
19290                .and_then(|metadata| metadata.first())
19291                .and_then(SignatureMetadata::callable_parameter_types),
19292            Some(["Vec256<T>".to_string()].as_slice())
19293        );
19294
19295        let fail = parsed
19296            .declarations()
19297            .iter()
19298            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
19299            .expect("explicit-object member");
19300        assert_eq!(fail.signature(), Some("(const this auto &)"));
19301        let metadata = parsed
19302            .signature_metadata
19303            .get(fail)
19304            .and_then(|metadata| metadata.first())
19305            .expect("explicit-object signature metadata");
19306        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
19307        assert!(
19308            metadata
19309                .callable_arity()
19310                .is_some_and(|arity| arity.accepts(0))
19311        );
19312    }
19313
19314    #[test]
19315    fn trailing_qualifiers_survive_parameter_list_whitespace() {
19316        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
19317        // declarator's structure, so an out-of-line definition that spells its
19318        // parameter list with different whitespace than the declaration must
19319        // still carry it.
19320        let source = r#"
19321struct Widget {
19322  bool multiline(int settings, int supprs) const;
19323  bool doublespace(int settings, int supprs) const;
19324  bool noexcept_multiline(int settings, int supprs) noexcept;
19325  bool ref_multiline(int settings, int supprs) &&;
19326};
19327bool
19328Widget::multiline (int settings,
19329                   int supprs) const
19330{ return settings + supprs > 0; }
19331bool Widget::doublespace(int settings,  int supprs) const { return true; }
19332bool Widget::noexcept_multiline(int settings,
19333                                int supprs) noexcept { return true; }
19334bool Widget::ref_multiline(int settings,
19335                           int supprs) && { return true; }
19336"#;
19337        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
19338        assert_eq!(
19339            vec!["(int, int) const".to_string()],
19340            identity_signatures(&parsed, "Widget.multiline")
19341        );
19342        assert_eq!(
19343            vec!["(int, int) const".to_string()],
19344            identity_signatures(&parsed, "Widget.doublespace")
19345        );
19346        assert_eq!(
19347            vec!["(int, int) noexcept".to_string()],
19348            identity_signatures(&parsed, "Widget.noexcept_multiline")
19349        );
19350        assert_eq!(
19351            vec!["(int, int) &&".to_string()],
19352            identity_signatures(&parsed, "Widget.ref_multiline")
19353        );
19354    }
19355
19356    #[test]
19357    fn macro_fragmented_plain_class_keeps_following_member_signature() {
19358        let source = r#"
19359struct CString {};
19360class CMessage {
19361public:
19362  CString GetParams(unsigned int index, unsigned int length = -1) const
19363      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
19364    return GetParamsColon(index, length);
19365  }
19366  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
19367};
19368CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
19369  return {};
19370}
19371"#;
19372        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
19373        assert_eq!(
19374            vec!["(unsigned int, unsigned int) const".to_string()],
19375            identity_signatures(&parsed, "CMessage.GetParamsColon")
19376        );
19377    }
19378
19379    #[test]
19380    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
19381        let source = r#"
19382#pragma once
19383#define DEMO_DEPRECATED(message)
19384namespace demo {
19385struct Base {
19386    static int aligned(int value) { return value; }
19387    int legacy(int value) const
19388        DEMO_DEPRECATED("use replacement()") { return value; }
19389    int replacement() const;
19390    void run(int value);
19391};
19392struct OtherBase {
19393    void run(int value);
19394    static int aligned(int value) { return value; }
19395};
19396struct Derived : Base {};
19397struct Override : Base {
19398    void run(int value);
19399    static int aligned(int value) { return value; }
19400};
19401struct RecoveredOverride : Base {
19402    int legacy(int value) const
19403        DEMO_DEPRECATED("use replacement()") { return value; }
19404    void run(int value);
19405};
19406struct Hidden : Base {
19407    void run(int first, int second);
19408    static int aligned(int first, int second) { return first + second; }
19409};
19410struct Ambiguous : Base, OtherBase {};
19411}
19412struct Global {};
19413"#;
19414        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
19415        let declarations = parsed.declarations();
19416        let fq_names = declarations
19417            .iter()
19418            .map(|unit| unit.fq_name())
19419            .collect::<std::collections::BTreeSet<_>>();
19420
19421        for expected in [
19422            "demo.Base",
19423            "demo.Base.aligned",
19424            "demo.Base.legacy",
19425            "demo.Base.replacement",
19426            "demo.Base.run",
19427            "demo.Derived",
19428            "demo.OtherBase",
19429            "demo.Override",
19430            "demo.RecoveredOverride",
19431            "demo.Hidden",
19432            "demo.Ambiguous",
19433            "Global",
19434        ] {
19435            assert!(
19436                fq_names.contains(expected),
19437                "missing {expected} from namespaced macro fragment: {declarations:#?}"
19438            );
19439        }
19440        assert!(
19441            !fq_names.contains("Derived"),
19442            "following class escaped its namespace: {declarations:#?}"
19443        );
19444        assert!(
19445            !fq_names.contains("demo.Global"),
19446            "global class crossed the recovered namespace boundary: {declarations:#?}"
19447        );
19448    }
19449
19450    #[test]
19451    fn trailing_qualifiers_still_separate_genuine_overloads() {
19452        // The qualifier must keep distinguishing the real C++ overload sets it
19453        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
19454        let source = r#"
19455struct Widget {
19456  int* slot(int index);
19457  const int* slot(int index) const;
19458  int log(int severity) &;
19459  int log(int severity) &&;
19460};
19461"#;
19462        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
19463        assert_eq!(
19464            vec!["(int)".to_string(), "(int) const".to_string()],
19465            identity_signatures(&parsed, "Widget.slot")
19466        );
19467        assert_eq!(
19468            vec!["(int) &".to_string(), "(int) &&".to_string()],
19469            identity_signatures(&parsed, "Widget.log")
19470        );
19471    }
19472
19473    #[test]
19474    fn virtual_specifier_is_not_part_of_the_identity_signature() {
19475        // `override` never appears on the out-of-line definition, and C++ does
19476        // not make it part of the signature, so it must not split the identity.
19477        let source = r#"
19478struct Base {
19479  virtual void run(int value) const;
19480};
19481struct Widget : Base {
19482  void run(int value) const override;
19483};
19484void Widget::run(int value) const {}
19485"#;
19486        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
19487        assert_eq!(
19488            vec!["(int) const".to_string()],
19489            identity_signatures(&parsed, "Widget.run")
19490        );
19491    }
19492
19493    #[test]
19494    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
19495        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
19496        // the function type, so a declaration that spells `const int` and a
19497        // definition that spells `int` are one entity.
19498        let source = r#"
19499struct Widget {
19500  bool value_params(const int settings, const int supprs);
19501  void pointee_const(const int* p);
19502  void pointer_const(int* const p);
19503  void both_const(const int* const p);
19504  void reference_const(const int& p);
19505  void array_const(const int values[4]);
19506};
19507bool Widget::value_params(int settings, int supprs) { return true; }
19508void Widget::pointer_const(int* p) {}
19509void Widget::both_const(const int* p) {}
19510"#;
19511        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
19512        assert_eq!(
19513            vec!["(int, int)".to_string()],
19514            identity_signatures(&parsed, "Widget.value_params")
19515        );
19516        assert_eq!(
19517            vec!["(int *)".to_string()],
19518            identity_signatures(&parsed, "Widget.pointer_const")
19519        );
19520        assert_eq!(
19521            vec!["(const int *)".to_string()],
19522            identity_signatures(&parsed, "Widget.both_const")
19523        );
19524        // The const that is not top-level still distinguishes the type.
19525        assert_eq!(
19526            vec!["(const int *)".to_string()],
19527            identity_signatures(&parsed, "Widget.pointee_const")
19528        );
19529        assert_eq!(
19530            vec!["(const int &)".to_string()],
19531            identity_signatures(&parsed, "Widget.reference_const")
19532        );
19533        assert_eq!(
19534            vec!["(const int [4])".to_string()],
19535            identity_signatures(&parsed, "Widget.array_const")
19536        );
19537    }
19538
19539    #[test]
19540    fn top_level_parameter_const_still_separates_pointee_overloads() {
19541        let source = r#"
19542struct Widget {
19543  void take(const int* p);
19544  void take(int* p);
19545};
19546"#;
19547        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
19548        assert_eq!(
19549            vec!["(const int *)".to_string(), "(int *)".to_string()],
19550            identity_signatures(&parsed, "Widget.take")
19551        );
19552    }
19553
19554    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
19555        let mut parser = tree_sitter::Parser::new();
19556        parser
19557            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19558            .unwrap();
19559        let tree = parser.parse(source, None).unwrap();
19560        let start = source.find(callable_name).expect("callable declaration");
19561        let declarator =
19562            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
19563        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
19564    }
19565
19566    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
19567        let mut shapes = comparable_shapes(source, callable_name);
19568        assert_eq!(1, shapes.len(), "{shapes:?}");
19569        match shapes.remove(0) {
19570            CppComparableSlot::Shape(shape) => shape,
19571            other => panic!("expected a comparable shape, got {other:?}"),
19572        }
19573    }
19574
19575    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
19576        let mut current = shape.root();
19577        loop {
19578            match shape.node(current) {
19579                CppComparableNode::Named { .. } => return shape.node(current),
19580                CppComparableNode::Pointer { inner, .. }
19581                | CppComparableNode::Reference { inner }
19582                | CppComparableNode::Array { inner } => current = *inner,
19583                CppComparableNode::Generic { base, .. } => current = *base,
19584            }
19585        }
19586    }
19587
19588    #[test]
19589    fn comparable_shape_keeps_pointee_const() {
19590        assert_ne!(
19591            sole_comparable_shape("void f(const char* p);", "f("),
19592            sole_comparable_shape("void f(char* p);", "f(")
19593        );
19594    }
19595
19596    #[test]
19597    fn comparable_shape_keeps_inner_pointer_const() {
19598        assert_ne!(
19599            sole_comparable_shape("void f(int** p);", "f("),
19600            sole_comparable_shape("void f(int* const* p);", "f(")
19601        );
19602    }
19603
19604    #[test]
19605    fn comparable_shape_drops_top_level_pointer_const() {
19606        assert_eq!(
19607            sole_comparable_shape("void f(int* const p);", "f("),
19608            sole_comparable_shape("void f(int* p);", "f(")
19609        );
19610    }
19611
19612    #[test]
19613    fn comparable_shape_drops_top_level_base_const() {
19614        assert_eq!(
19615            sole_comparable_shape("void f(const int p);", "f("),
19616            sole_comparable_shape("void f(int p);", "f(")
19617        );
19618    }
19619
19620    #[test]
19621    fn comparable_shape_decays_top_level_array_to_pointer() {
19622        assert_eq!(
19623            sole_comparable_shape("void f(int a[3]);", "f("),
19624            sole_comparable_shape("void f(int* a);", "f(")
19625        );
19626        assert_eq!(
19627            sole_comparable_shape("void f(int* a[3]);", "f("),
19628            sole_comparable_shape("void f(int** a);", "f(")
19629        );
19630    }
19631
19632    #[test]
19633    fn comparable_shape_keeps_array_behind_pointer() {
19634        assert_ne!(
19635            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
19636            sole_comparable_shape("struct S { void f(int** a); };", "f(")
19637        );
19638    }
19639
19640    #[test]
19641    fn comparable_shape_records_written_name_and_lexical_scope() {
19642        let declared =
19643            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
19644        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
19645        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
19646            panic!("named leaf");
19647        };
19648        assert_eq!(["Msg".to_string()].as_slice(), name.path());
19649        assert_eq!(
19650            ["ns".to_string(), "S".to_string()].as_slice(),
19651            name.lexical_scope()
19652        );
19653        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
19654            panic!("named leaf");
19655        };
19656        assert_eq!(
19657            ["ns".to_string(), "Msg".to_string()].as_slice(),
19658            name.path()
19659        );
19660        assert!(name.lexical_scope().is_empty());
19661        assert_ne!(declared, defined);
19662    }
19663
19664    #[test]
19665    fn comparable_shape_marks_sized_primitive_leaf() {
19666        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
19667        let CppComparableNode::Named {
19668            name, primitive, ..
19669        } = comparable_named_leaf(&shape)
19670        else {
19671            panic!("named leaf");
19672        };
19673        assert!(primitive);
19674        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
19675        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
19676    }
19677
19678    #[test]
19679    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
19680        assert_eq!(
19681            vec![CppComparableSlot::Unstructured],
19682            comparable_shapes("void f(void (*cb)(int));", "f(")
19683        );
19684    }
19685
19686    #[test]
19687    fn comparable_shape_reports_ellipsis_slot() {
19688        let shapes = comparable_shapes("void f(int a, ...);", "f(");
19689        assert_eq!(2, shapes.len(), "{shapes:?}");
19690        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
19691    }
19692
19693    #[test]
19694    fn comparable_shape_keeps_template_argument_const() {
19695        assert_ne!(
19696            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
19697            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
19698        );
19699    }
19700
19701    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
19702    /// file-scope tag that a later `struct inner *` at file scope may name.
19703    #[test]
19704    fn c_file_mints_aggregate_member_tag_at_file_scope() {
19705        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
19706        let parsed = parse_cpp_declarations(source, "x.c");
19707        let declarations = parsed.declarations();
19708
19709        assert!(
19710            declarations
19711                .iter()
19712                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
19713            "expected a file-scope inner tag, got {declarations:?}"
19714        );
19715        assert!(
19716            declarations
19717                .iter()
19718                .all(|unit| unit.fq_name() != "outer$inner"),
19719            "expected no nested identity, got {declarations:?}"
19720        );
19721        assert!(
19722            declarations
19723                .iter()
19724                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
19725        );
19726        // Members still belong to their own aggregate.
19727        assert!(
19728            declarations
19729                .iter()
19730                .any(|unit| unit.fq_name() == "inner.value")
19731        );
19732        assert!(
19733            declarations
19734                .iter()
19735                .any(|unit| unit.fq_name() == "outer.item")
19736        );
19737
19738        let outer = declarations
19739            .iter()
19740            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
19741            .expect("outer");
19742        assert!(
19743            parsed
19744                .children
19745                .get(outer)
19746                .into_iter()
19747                .flatten()
19748                .all(|child| child.fq_name() != "inner"),
19749            "the tag must not hang off the aggregate it is written inside: {:?}",
19750            parsed.children
19751        );
19752    }
19753
19754    /// A header carries no compilation language of its own, and a `.cpp`
19755    /// translation unit really does declare a nested class. Both keep exactly
19756    /// the C++ extraction they had before the C dialect existed.
19757    #[test]
19758    fn header_and_cpp_files_keep_nested_tag_identity() {
19759        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
19760        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
19761            let parsed = parse_cpp_declarations(source, name);
19762            let declarations = parsed.declarations();
19763            assert!(
19764                declarations
19765                    .iter()
19766                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
19767                "{name} must keep the nested identity, got {declarations:?}"
19768            );
19769            assert!(
19770                declarations.iter().all(|unit| unit.fq_name() != "inner"),
19771                "{name} must not mint a file-scope tag, got {declarations:?}"
19772            );
19773            assert!(
19774                declarations
19775                    .iter()
19776                    .any(|unit| unit.fq_name() == "outer$inner.value")
19777            );
19778        }
19779    }
19780
19781    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
19782    #[test]
19783    fn uppercase_c_extension_keeps_cpp_tag_scope() {
19784        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
19785        let parsed = parse_cpp_declarations(source, "x.C");
19786        assert!(
19787            parsed
19788                .declarations()
19789                .iter()
19790                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
19791        );
19792    }
19793
19794    /// There is no such thing as a partially nested tag in C: every level of a
19795    /// nested aggregate chain lands at the same enclosing scope.
19796    #[test]
19797    fn c_file_mints_every_nesting_level_at_file_scope() {
19798        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
19799        let parsed = parse_cpp_declarations(source, "z.c");
19800        let declarations = parsed.declarations();
19801
19802        for tag in ["a", "b", "c"] {
19803            assert!(
19804                declarations
19805                    .iter()
19806                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
19807                "expected a file-scope {tag}, got {declarations:?}"
19808            );
19809        }
19810        assert!(
19811            declarations
19812                .iter()
19813                .all(|unit| !unit.fq_name().contains('$')),
19814            "no level may keep a nested identity, got {declarations:?}"
19815        );
19816        // Each member still belongs to the aggregate that declares it.
19817        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
19818        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
19819        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
19820    }
19821
19822    /// An enum tag is a tag; its enumerators stay members of the enum, which is
19823    /// what makes them ordinary identifiers at the enum's own (file) scope.
19824    #[test]
19825    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
19826        let source = "struct outer { enum color { RED, GREEN } c; };\n";
19827        let parsed = parse_cpp_declarations(source, "e.c");
19828        let declarations = parsed.declarations();
19829
19830        let color = declarations
19831            .iter()
19832            .find(|unit| unit.is_class() && unit.fq_name() == "color")
19833            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
19834        assert!(
19835            declarations
19836                .iter()
19837                .all(|unit| unit.fq_name() != "outer$color")
19838        );
19839        for enumerator in ["color.RED", "color.GREEN"] {
19840            assert!(
19841                declarations.iter().any(|unit| unit.fq_name() == enumerator),
19842                "expected {enumerator}, got {declarations:?}"
19843            );
19844        }
19845        let children = parsed
19846            .children
19847            .get(color)
19848            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
19849        assert!(
19850            ["color.RED", "color.GREEN"]
19851                .iter()
19852                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
19853            "enumerators must hang off their enum: {children:?}"
19854        );
19855    }
19856
19857    #[test]
19858    fn c_file_mints_member_list_union_at_file_scope() {
19859        let source = "struct outer { union inner { int a; float b; } item; };\n";
19860        let parsed = parse_cpp_declarations(source, "u.c");
19861        let declarations = parsed.declarations();
19862        assert!(
19863            declarations
19864                .iter()
19865                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
19866            "expected a file-scope inner union, got {declarations:?}"
19867        );
19868        assert!(
19869            declarations
19870                .iter()
19871                .all(|unit| unit.fq_name() != "outer$inner")
19872        );
19873        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
19874        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
19875    }
19876
19877    /// A tag declared in a namespace member list is not a file-scope tag: the
19878    /// nearest enclosing non-aggregate scope is the namespace.
19879    #[test]
19880    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
19881        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
19882        let parsed = parse_cpp_declarations(source, "n.c");
19883        let declarations = parsed.declarations();
19884        let inner = declarations
19885            .iter()
19886            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
19887            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
19888        assert_eq!(inner.package_name(), "ns");
19889        assert!(
19890            declarations
19891                .iter()
19892                .all(|unit| unit.fq_name() != "ns.outer$inner")
19893        );
19894    }
19895
19896    /// Pins today's treatment of a tag declared inside a function body: the
19897    /// declaration walk does not descend into statement bodies, so no unit is
19898    /// minted for it in either dialect. C block scope is out of scope for the
19899    /// dialect change, and this test proves the change did not disturb it.
19900    #[test]
19901    fn function_local_tags_are_unchanged_in_both_dialects() {
19902        let source =
19903            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
19904        for name in ["y.c", "y.cpp"] {
19905            let parsed = parse_cpp_declarations(source, name);
19906            let declarations = parsed.declarations();
19907            assert!(
19908                declarations
19909                    .iter()
19910                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
19911                "{name}: {declarations:?}"
19912            );
19913            for tag in ["localtag", "deeper", "localtag$deeper"] {
19914                assert!(
19915                    declarations.iter().all(|unit| unit.fq_name() != tag),
19916                    "{name} must not mint {tag}, got {declarations:?}"
19917                );
19918            }
19919        }
19920    }
19921
19922    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
19923    /// re-scope: the typedef name is identical in both dialects.
19924    #[test]
19925    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
19926        let source = "typedef struct { int v; } T;\n";
19927        for name in ["t.c", "t.cpp"] {
19928            let parsed = parse_cpp_declarations(source, name);
19929            let declarations = parsed.declarations();
19930            assert!(
19931                declarations
19932                    .iter()
19933                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
19934                "{name}: {declarations:?}"
19935            );
19936        }
19937    }
19938
19939    #[test]
19940    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
19941        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
19942        let parsed = parse_cpp_declarations(source, "socket.c");
19943        let declarations = parsed.declarations();
19944        assert_eq!(
19945            declarations
19946                .iter()
19947                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
19948                .count(),
19949            1,
19950            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
19951        );
19952        for expected in [
19953            "PAL_HANDLE",
19954            "PAL_HANDLE.sock",
19955            "PAL_HANDLE$sock",
19956            "PAL_HANDLE$sock.ops",
19957        ] {
19958            assert!(
19959                declarations.iter().any(|unit| unit.fq_name() == expected),
19960                "expected {expected}, got {declarations:?}"
19961            );
19962        }
19963    }
19964
19965    /// `class` is not C. Source that spells one in a `.c` file is not C code,
19966    /// so it keeps the C++ reading rather than acquiring a half-C identity.
19967    #[test]
19968    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
19969        let source = "class outer { class inner { int v; }; };\n";
19970        let c_parsed = parse_cpp_declarations(source, "k.c");
19971        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
19972        let c_declarations = c_parsed.declarations();
19973        let cpp_declarations = cpp_parsed.declarations();
19974        assert!(
19975            c_declarations
19976                .iter()
19977                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
19978            "{c_declarations:?}"
19979        );
19980        assert_eq!(
19981            c_declarations
19982                .iter()
19983                .map(|unit| unit.fq_name())
19984                .collect::<std::collections::BTreeSet<_>>(),
19985            cpp_declarations
19986                .iter()
19987                .map(|unit| unit.fq_name())
19988                .collect::<std::collections::BTreeSet<_>>()
19989        );
19990    }
19991
19992    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
19993    /// every (node, class-like name) pair a tree offers, and require the same
19994    /// answer from both.
19995    ///
19996    /// The release build has no `debug_assertions` agreement check, so this is
19997    /// what pins the two together there.  Both query orders are exercised:
19998    /// document order is what the walk does, and reverse order proves that a
19999    /// question about an earlier byte than one already answered is still
20000    /// filtered back to its own prefix rather than answered from the wider
20001    /// fold.
20002    ///
20003    /// Returns how many questions were answered with a namespace, so a fixture
20004    /// can assert it actually reached the path (#2754).
20005    fn namespace_forward_scan_agreement(source: &str) -> usize {
20006        let mut parser = tree_sitter::Parser::new();
20007        parser
20008            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20009            .unwrap();
20010        let tree = parser.parse(source, None).unwrap();
20011        let root = tree.root_node();
20012        let ancestry = ParentIndex::new(root);
20013
20014        let mut nodes = Vec::new();
20015        let mut names = std::collections::BTreeSet::new();
20016        let mut cursor = root.walk();
20017        let mut stack = vec![root];
20018        while let Some(node) = stack.pop() {
20019            if matches!(
20020                node.kind(),
20021                "class_specifier" | "struct_specifier" | "union_specifier"
20022            ) && let Some(name) = class_like_name(node, source, &ancestry)
20023            {
20024                names.insert(name);
20025            }
20026            nodes.push(node);
20027            stack.extend(node.named_children(&mut cursor));
20028        }
20029        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
20030        assert!(!names.is_empty(), "fixture declares no class-like name");
20031
20032        let mut answered = 0usize;
20033        for reversed in [false, true] {
20034            let mut scan = CppNamespaceForwardScan::default();
20035            let ordered: Vec<_> = if reversed {
20036                nodes.iter().rev().copied().collect()
20037            } else {
20038                nodes.clone()
20039            };
20040            answered = 0;
20041            for node in ordered {
20042                for name in &names {
20043                    scan.advance_to(root, node.start_byte(), source, &ancestry);
20044                    let carried = scan.unique_earlier_forward(name, node);
20045                    assert_eq!(
20046                        carried,
20047                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
20048                        "carried-forward scan and prefix scan disagree about {name} at \
20049                         {} node starting at byte {} (reversed order: {reversed})",
20050                        node.kind(),
20051                        node.start_byte()
20052                    );
20053                    answered += usize::from(carried.is_some());
20054                }
20055            }
20056        }
20057        answered
20058    }
20059
20060    /// A malformed namespace whose forward declarations are the only identity
20061    /// signal left for the class definitions tree-sitter pushed out to file
20062    /// scope.  Both recovered classes open the guard; only the first one is
20063    /// separated from the namespace by nothing but recovery trivia, so only the
20064    /// first one borrows.  The carried-forward scan has to reproduce both
20065    /// answers.
20066    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
20067namespace ns {
20068class Widget;
20069class Gadget;
20070int x = ;
20071}
20072class API Widget {
20073public:
20074    void first();
20075};
20076class API Gadget {
20077public:
20078    void second();
20079};
20080"#;
20081
20082    #[test]
20083    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
20084        assert!(
20085            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
20086            "the fixture must actually reach the namespace-borrow path"
20087        );
20088
20089        // Nothing here may be answered, and the two paths have to agree about
20090        // that too: a clean namespace is not an identity proof, two forwards of
20091        // one name are ambiguous rather than a guess, and a forward inside a
20092        // function body is not at namespace scope.
20093        for source in [
20094            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
20095            r#"#define API
20096namespace ns {
20097class Widget;
20098class Widget;
20099int x = ;
20100}
20101class API Widget {
20102public:
20103    void method();
20104};
20105"#,
20106            r#"#define API
20107namespace ns {
20108void host() {
20109    class Widget;
20110}
20111int x = ;
20112}
20113class API Widget {
20114public:
20115    void method();
20116};
20117"#,
20118        ] {
20119            assert_eq!(
20120                namespace_forward_scan_agreement(source),
20121                0,
20122                "no borrow is justified here: {source}"
20123            );
20124        }
20125    }
20126
20127    /// The fold is incremental, so a walk that asks about steadily later bytes
20128    /// must never re-fold a node an earlier question already folded, and must
20129    /// never skip one that lies between two questions.
20130    #[test]
20131    fn carried_forward_namespace_scan_folds_each_node_once() {
20132        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
20133        let mut parser = tree_sitter::Parser::new();
20134        parser
20135            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20136            .unwrap();
20137        let tree = parser.parse(source, None).unwrap();
20138        let root = tree.root_node();
20139        let ancestry = ParentIndex::new(root);
20140
20141        let mut incremental = CppNamespaceForwardScan::default();
20142        for cutoff in 0..=source.len() {
20143            incremental.advance_to(root, cutoff, source, &ancestry);
20144        }
20145        let mut whole = CppNamespaceForwardScan::default();
20146        whole.advance_to(root, source.len(), source, &ancestry);
20147
20148        let mut incremental_shape: Vec<_> = incremental
20149            .forwards
20150            .iter()
20151            .map(|(name, forwards)| {
20152                (
20153                    name.clone(),
20154                    forwards
20155                        .iter()
20156                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
20157                        .collect::<Vec<_>>(),
20158                )
20159            })
20160            .collect();
20161        let mut whole_shape: Vec<_> = whole
20162            .forwards
20163            .iter()
20164            .map(|(name, forwards)| {
20165                (
20166                    name.clone(),
20167                    forwards
20168                        .iter()
20169                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
20170                        .collect::<Vec<_>>(),
20171                )
20172            })
20173            .collect();
20174        incremental_shape.sort();
20175        whole_shape.sort();
20176        for (_, forwards) in &mut incremental_shape {
20177            forwards.sort();
20178        }
20179        for (_, forwards) in &mut whole_shape {
20180            forwards.sort();
20181        }
20182
20183        assert!(!whole_shape.is_empty(), "fixture folds no forward");
20184        assert_eq!(
20185            incremental_shape, whole_shape,
20186            "one byte at a time must fold exactly what one whole pass folds"
20187        );
20188    }
20189
20190    /// Drive the region reparse and the whitespace-padded reparse it replaced
20191    /// over the same region and require identical trees.
20192    ///
20193    /// The release build has no `debug_assertions` agreement check, so this is
20194    /// what pins the two together there. Each body is reparsed at its own
20195    /// offset and again after a long prefix, because the prefix is the whole
20196    /// difference between the two techniques: the padded parse lexes it as
20197    /// whitespace, the included-range parse never sees it, and the tree has to
20198    /// come out the same either way (#2788).
20199    fn fragmented_class_reparse_agreement(body: &str) {
20200        for prefix in [
20201            String::new(),
20202            "// leading comment\n".to_string(),
20203            // A body starts just after its class head's `{`, which is normally
20204            // mid-line: the padded parse then has spaces before the region on
20205            // the region's own line, and the included-range parse has nothing
20206            // at all before it.
20207            "class Widget : public Base { ".to_string(),
20208            "namespace filler {\n".to_string()
20209                + &"struct Filler { int member; };\n".repeat(200)
20210                + "}\n",
20211            "namespace filler {\n".to_string()
20212                + &"struct Filler { int member; };\n".repeat(200)
20213                + "}\nclass Widget : public Base { ",
20214        ] {
20215            let source = format!("{prefix}{body}");
20216            let start = prefix.len();
20217            let end = source.len();
20218            let region = cpp_reparse_fragmented_class_body(&source, start, end)
20219                .expect("the region reparse must produce a tree");
20220            let padded = cpp_reparse_padded_class_body(&source, start, end)
20221                .expect("the padded reparse must produce a tree");
20222            assert_eq!(
20223                cpp_tree_shape(&region),
20224                cpp_tree_shape(&padded),
20225                "region and padded reparse disagree at offset {start} of {end} bytes"
20226            );
20227            assert_eq!(
20228                region.root_node().start_byte(),
20229                start,
20230                "the reparsed region keeps its original offsets"
20231            );
20232        }
20233    }
20234
20235    #[test]
20236    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
20237        // A conditional immediately after an access label: the shape the padded
20238        // technique was kept for, because the directive and its macro name
20239        // become an ERROR plus the following declaration's apparent type.
20240        fragmented_class_reparse_agreement(
20241            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
20242        );
20243        fragmented_class_reparse_agreement(
20244            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
20245        );
20246        // The merged inline constructor and the nested fragmented bodies the
20247        // #938 recovery reads out of a reparse.
20248        fragmented_class_reparse_agreement(
20249            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
20250        );
20251        fragmented_class_reparse_agreement(
20252            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
20253        );
20254        // A body that is not member-shaped at all still has to reparse the same
20255        // way, because the admission gate reads the tree to reject it.
20256        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
20257    }
20258
20259    /// A header shaped like the generated ones this walk is slow on: many
20260    /// enums, classes whose members share the enums' names, nested enums, an
20261    /// ownerless enumerator, and namespaced repeats of all of it.
20262    fn many_enums_and_mixed_declarations() -> String {
20263        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
20264        for index in 0..40 {
20265            let _ = write!(
20266                source,
20267                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
20268                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
20269                 class Color{index}Like {{ public: int member{index}; }};\n"
20270            );
20271        }
20272        source.push_str("namespace outer {\n");
20273        for index in 0..20 {
20274            let _ = write!(
20275                source,
20276                "enum Shade{index} {{ DARK{index} }};\n\
20277                 struct Shade{index}Holder {{ int field{index}; }};\n"
20278            );
20279        }
20280        source.push_str("}\n");
20281        source
20282    }
20283
20284    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
20285    /// every question a fixture's declarations can ask, and require the same
20286    /// answer from both.
20287    ///
20288    /// The release build has no `debug_assertions` agreement check, so this is
20289    /// what pins the two together there. The index is fed one declaration at a
20290    /// time and every question is re-asked after each one, which is what proves
20291    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
20292    /// test. Each of the fixture's own fields is also fed in restated as a
20293    /// declaration of another file: the scan ignores those because it asks
20294    /// about the asking unit's own source, and the index has to ignore them for
20295    /// the same reason.
20296    ///
20297    /// Returns how many questions the fixture answered `true`, so a caller can
20298    /// assert that it actually reached the path (#2786).
20299    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
20300        let parsed = parse_cpp_declarations(source, name);
20301        let file = ProjectFile::new(std::env::temp_dir(), name);
20302        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
20303
20304        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
20305        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
20306
20307        let foreign: Vec<CodeUnit> = declarations
20308            .iter()
20309            .filter(|unit| unit.kind() == CodeUnitType::Field)
20310            .map(|unit| {
20311                CodeUnit::new_fq(
20312                    elsewhere.clone(),
20313                    unit.kind(),
20314                    unit.package_name().to_string(),
20315                    unit.short_name().to_string(),
20316                    unit.fq().clone(),
20317                )
20318            })
20319            .collect();
20320
20321        // One owner chain deeper than any C++ short name reaches today
20322        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
20323        // the member). The scan asks `starts_with("owner.")`, so a field like
20324        // this answers for every owner in its chain, and the index has to
20325        // record every one of them rather than only the innermost.
20326        let mut packages: Vec<String> = declarations
20327            .iter()
20328            .map(|unit| unit.package_name().to_string())
20329            .collect();
20330        packages.push(String::new());
20331        packages.sort();
20332        packages.dedup();
20333        let deeper: Vec<CodeUnit> = packages
20334            .iter()
20335            .map(|package_name| {
20336                CodeUnit::new_fq(
20337                    file.clone(),
20338                    CodeUnitType::Field,
20339                    package_name.clone(),
20340                    "SynthOwner.middle.leaf".to_string(),
20341                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
20342                )
20343            })
20344            .collect();
20345
20346        // Every (package, owner) pair anything could ask about: each unit's own
20347        // short name, each dotted prefix of it, and the empty owner an
20348        // anonymous enum asks with (#2140).
20349        let mut questions: Vec<(String, String)> = Vec::new();
20350        for unit in declarations.iter().chain(deeper.iter()) {
20351            let package_name = unit.package_name().to_string();
20352            let short_name = unit.short_name();
20353            questions.push((package_name.clone(), short_name.to_string()));
20354            questions.push((package_name.clone(), String::new()));
20355            for (offset, _) in short_name.match_indices('.') {
20356                questions.push((package_name.clone(), short_name[..offset].to_string()));
20357            }
20358        }
20359        questions.sort();
20360        questions.dedup();
20361
20362        let mut index = CppFieldOwnerIndex::default();
20363        let mut recorded: Vec<&CodeUnit> = Vec::new();
20364        let mut answered = 0usize;
20365        for unit in foreign
20366            .iter()
20367            .chain(declarations.iter())
20368            .chain(deeper.iter())
20369        {
20370            index.record(unit, &file);
20371            recorded.push(unit);
20372            for (package_name, owner_short_name) in &questions {
20373                let carried = index.owns_fields(package_name, owner_short_name);
20374                assert_eq!(
20375                    carried,
20376                    cpp_declarations_hold_owned_fields(
20377                        recorded.iter().copied(),
20378                        &file,
20379                        package_name,
20380                        owner_short_name
20381                    ),
20382                    "the carried field index and the declaration scan disagree about \
20383                     {package_name:?}/{owner_short_name:?} after recording {}",
20384                    unit.fq_name()
20385                );
20386                answered += usize::from(carried);
20387            }
20388        }
20389
20390        // The whole-set build the first question performs must land on the same
20391        // index the incremental record built.
20392        let rebuilt = CppFieldOwnerIndex::of(
20393            foreign
20394                .iter()
20395                .chain(declarations.iter())
20396                .chain(deeper.iter()),
20397            &file,
20398        );
20399        for (package_name, owner_short_name) in &questions {
20400            assert_eq!(
20401                rebuilt.owns_fields(package_name, owner_short_name),
20402                index.owns_fields(package_name, owner_short_name),
20403                "a rebuilt index must answer what the incremental one answers for \
20404                 {package_name:?}/{owner_short_name:?}"
20405            );
20406        }
20407        answered
20408    }
20409
20410    /// The one thing the field index cannot absorb by addition: a deferred
20411    /// replacement of a declaration that owns children removes those children.
20412    ///
20413    /// The first enum builds the index, the struct records `Color.RED` into it,
20414    /// the body-less second `Color` replaces the first and takes `Color.RED`
20415    /// with it, and the last enum then asks about owner `Color`. An index that
20416    /// survived that removal answers `true` where the declarations say `false`,
20417    /// which is exactly what the in-walk agreement assertion catches (#2786).
20418    #[test]
20419    fn a_replacement_that_removes_children_drops_the_field_index() {
20420        let source =
20421            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
20422        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
20423        let mut names: Vec<_> = parsed
20424            .declarations()
20425            .iter()
20426            .map(|unit| unit.fq_name())
20427            .collect();
20428        names.sort();
20429        assert_eq!(
20430            names,
20431            vec![
20432                "Color".to_string(),
20433                "First".to_string(),
20434                "First.A".to_string()
20435            ],
20436            "the replaced Color owns no field any more"
20437        );
20438    }
20439
20440    /// A recovery that re-declares what the file already declared mints
20441    /// nothing.
20442    ///
20443    /// The reparse walk replaces the outer `Widget`, which removes its method,
20444    /// and then re-creates that method from the region. Creation alone would
20445    /// call the method recovered; it was there before the recovery opened, so
20446    /// the recovered set is empty and only the region's own reparse window is
20447    /// recorded (#2787).
20448    #[test]
20449    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
20450        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
20451                      BEGIN_NS\n\
20452                      namespace demo { struct Widget { void doWork(); }; }\n\
20453                      END_NS\n";
20454        let parsed = parse_cpp_declarations(source, "restored.cpp");
20455        let recovered: Vec<String> = parsed
20456            .materialization_records
20457            .iter()
20458            .filter_map(|record| match record {
20459                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
20460                _ => None,
20461            })
20462            .collect();
20463        assert!(
20464            recovered.is_empty(),
20465            "the region declares nothing the file did not already declare: {recovered:?}"
20466        );
20467        let mut names: Vec<String> = parsed
20468            .declarations()
20469            .iter()
20470            .map(|unit| unit.fq_name())
20471            .collect();
20472        names.sort();
20473        assert_eq!(
20474            names,
20475            vec![
20476                "demo".to_string(),
20477                "demo.Widget".to_string(),
20478                "demo.Widget.doWork".to_string(),
20479            ]
20480        );
20481    }
20482
20483    /// Four macro-sentinel recoveries in one file (#941). Each one must record
20484    /// exactly the declarations it minted -- not the ones an earlier recovery
20485    /// minted, and not the file's other declarations -- in start-byte order,
20486    /// and each record must carry its own reparse window (#2787).
20487    #[test]
20488    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
20489        let mut source = String::new();
20490        for index in 0..4 {
20491            let _ = write!(
20492                source,
20493                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
20494            );
20495        }
20496        source.push_str("void outside() {}\n");
20497        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
20498
20499        let recovered: Vec<(String, (usize, usize))> = parsed
20500            .materialization_records
20501            .iter()
20502            .filter_map(|record| match record {
20503                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
20504                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
20505                }
20506                _ => None,
20507            })
20508            .collect();
20509
20510        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
20511        for index in 0..4 {
20512            // The window the reparse covers: everything after the opening
20513            // sentinel token up to the newline before the closing one.
20514            let region = format!("namespace demo{index}");
20515            let region_start = source.find(&region).expect("each region is in the source");
20516            let start = source[..region_start]
20517                .rfind("BEGIN_NS")
20518                .expect("each region opens with a sentinel")
20519                + "BEGIN_NS".len();
20520            let end = start
20521                + source[start..]
20522                    .find("END_NS")
20523                    .expect("each region closes with a sentinel")
20524                - 1;
20525            let window = (start, end);
20526            for name in [
20527                format!("demo{index}"),
20528                format!("demo{index}.Widget{index}"),
20529                format!("demo{index}.Widget{index}.doWork{index}"),
20530            ] {
20531                expected.push((name, window));
20532            }
20533        }
20534        assert_eq!(
20535            recovered, expected,
20536            "each recovery records its own minted declarations, in order"
20537        );
20538        assert!(
20539            parsed
20540                .declarations()
20541                .iter()
20542                .any(|unit| unit.fq_name() == "outside"),
20543            "the declaration outside every region stays parsed and unrecovered"
20544        );
20545    }
20546
20547    #[test]
20548    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
20549        assert!(
20550            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
20551            "the fixture must actually own fields"
20552        );
20553
20554        // The shapes that make the two implementations diverge if the index
20555        // records the wrong keys: a nested enum's owner is its whole dotted
20556        // chain, a class named like an enum owns fields under that same name, a
20557        // `$` in a short name is an owner separator the dotted prefix rule must
20558        // not split on, and the same enum name in two namespaces is two owners.
20559        for (source, name) in [
20560            ("struct S { enum E { V }; };\n", "nested.hpp"),
20561            (
20562                "enum Color { RED };\nstruct Color { int RED; };\n",
20563                "class-like.c",
20564            ),
20565            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
20566            (
20567                "enum E { V };\nnamespace ns { enum E { V }; }\n",
20568                "repeated.hpp",
20569            ),
20570            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
20571        ] {
20572            field_owner_index_agreement(source, name);
20573        }
20574    }
20575
20576    /// `blocks` copies of one plain class-with-methods template. The class the
20577    /// question is about is always the first one, so the only thing that
20578    /// changes between two of these sources is how much unrelated tree
20579    /// surrounds it.
20580    fn repeated_class_blocks(blocks: usize) -> String {
20581        let mut source = String::from("namespace demo {\n");
20582        for index in 0..blocks {
20583            let _ = write!(
20584                source,
20585                "\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"
20586            );
20587        }
20588        source.push_str("\n}\n");
20589        source
20590    }
20591
20592    /// #1496: asking whether a recovered class shape claims one range must cost
20593    /// the path to that range, not a pass over the whole translation unit.
20594    ///
20595    /// The C++ inverse scan asks this once per candidate type reference, so a
20596    /// whole-tree walk makes one file's scan quadratic in its own size. Both
20597    /// sources here answer `None` -- these are plain classes that no recovery
20598    /// shape claims -- which is exactly the case that used to pay full price.
20599    #[test]
20600    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
20601        let mut answers = Vec::new();
20602        let mut visits = Vec::new();
20603        let mut node_counts = Vec::new();
20604        for blocks in [200usize, 400] {
20605            let source = repeated_class_blocks(blocks);
20606            let mut parser = tree_sitter::Parser::new();
20607            parser
20608                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20609                .unwrap();
20610            let tree = parser.parse(&source, None).unwrap();
20611            let start_byte = source.find("class Widget0 ").expect("first class");
20612            let end_byte = start_byte
20613                + source[start_byte..]
20614                    .find("};")
20615                    .expect("first class terminator")
20616                + "};".len();
20617            let range = Range {
20618                start_byte,
20619                end_byte,
20620                start_line: 0,
20621                end_line: 0,
20622            };
20623            reset_recovered_class_body_node_visits_for_test();
20624            let recovered_export_classes =
20625                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
20626            answers.push(recovered_class_body_at(
20627                &recovered_export_classes,
20628                tree.root_node(),
20629                &source,
20630                "Widget0",
20631                &range,
20632            ));
20633            visits.push(recovered_class_body_node_visits_for_test());
20634            let mut nodes = 0usize;
20635            let mut stack = vec![tree.root_node()];
20636            while let Some(node) = stack.pop() {
20637                nodes += 1;
20638                let mut cursor = node.walk();
20639                stack.extend(node.named_children(&mut cursor));
20640            }
20641            node_counts.push(nodes);
20642        }
20643
20644        assert_eq!(
20645            answers,
20646            vec![None, None],
20647            "no recovered shape claims a plain class"
20648        );
20649        assert_eq!(
20650            visits[0], visits[1],
20651            "the walk must follow the range's own path, so doubling the unrelated \
20652             classes must not change the node count: {visits:?} over trees of \
20653             {node_counts:?} nodes"
20654        );
20655        assert!(
20656            visits[1] * 20 < node_counts[1],
20657            "the walk must stay far below one pass over the tree: {visits:?} over \
20658             trees of {node_counts:?} nodes"
20659        );
20660    }
20661
20662    #[test]
20663    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
20664        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
20665        let mut parser = tree_sitter::Parser::new();
20666        parser
20667            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20668            .expect("C++ grammar");
20669        let tree = parser.parse(source, None).expect("fixture tree");
20670        let mut stack = vec![tree.root_node()];
20671        let mut recovered = None;
20672        while let Some(node) = stack.pop() {
20673            if node.kind() == "field_declaration"
20674                && let Some(field) = recovered_function_like_field_declarator(node, source)
20675            {
20676                recovered = Some((node, field.name));
20677                break;
20678            }
20679            let mut cursor = node.walk();
20680            stack.extend(node.named_children(&mut cursor));
20681        }
20682        let (declaration, name) = recovered.unwrap_or_else(|| {
20683            panic!(
20684                "pointer-wrapped macro field was not recovered: {}",
20685                tree.root_node().to_sexp()
20686            )
20687        });
20688        assert_eq!(node_text(name, source), "handshake");
20689        let recovered =
20690            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
20691        assert_eq!(recovered.pointer_depth(), 1);
20692        assert_eq!(
20693            render_cpp_field_signature(declaration, name, source),
20694            "struct handshake * handshake;"
20695        );
20696    }
20697}