Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use 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::{
27    NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
28    push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
29};
30use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
31use brokk_bifrost_core::hash::{HashMap, HashSet};
32use regex::Regex;
33use tree_sitter::{Node, Parser, Tree};
34
35/// Intern one qualified-name segment in the process-global interner.
36fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
37    segment_interner().intern(text, kind)
38}
39
40/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
41/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
42/// `::` head is exactly the mixed-separator store issue #1163 is about; the
43/// structured form records each namespace component, and the equivalence check
44/// renders it natively (with `::` between adjacent Package segments) so it
45/// round-trips to the legacy string. Splitting the already-joined string here is
46/// the M1 bridge — the legacy strings stay authoritative until M3.
47fn cpp_push_package(fq: &mut FqName, package_name: &str) {
48    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
49        fq.push(cpp_segment(component, SegmentKind::Package));
50    }
51}
52
53/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
54const CPP_PACKAGE_SEPARATOR: &str = "::";
55
56/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
57/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
58/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
59/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
60/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
61/// cpp-specific native rendering rule is needed for this chain.
62fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
63    let mut first = true;
64    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
65    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
66    // entry point, not re-inference of an already-structured name.
67    for component in chain.split('$').filter(|c| !c.is_empty()) {
68        let kind = if first {
69            SegmentKind::Type
70        } else {
71            SegmentKind::Nested
72        };
73        fq.push(cpp_segment(component, kind));
74        first = false;
75    }
76}
77
78/// Structured name for a C++ namespace module: every `::`-separated component is
79/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
80/// `short_name` with an empty `package_name`).
81fn cpp_namespace_fq(full_name: &str) -> FqName {
82    let mut fq = FqName::new();
83    cpp_push_package(&mut fq, full_name);
84    fq
85}
86
87/// The per-level namespace components a `namespace_definition`'s `name` field
88/// declares.
89///
90/// A C++17 nested definition (`namespace a::b::c`) parses as a
91/// `nested_namespace_specifier` whose named children are the per-level
92/// `namespace_identifier`s plus, for three or more levels, a further
93/// `nested_namespace_specifier`; the `::` separators, the optional per-level
94/// `inline`, and the leading global `::` are all anonymous tokens the walk
95/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
96/// segment path as the expanded `namespace a { namespace b { } }` form.
97///
98/// A shape outside that grammar is the deliberately ill-formed source the
99/// diagnostic corpora carry. Those keep their historical single-component
100/// reading of the raw name text, which the caller still joins to the lexical
101/// namespace exactly as before.
102fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
103    let mut components = Vec::new();
104    let mut stack = vec![node];
105    while let Some(current) = stack.pop() {
106        match current.kind() {
107            "namespace_identifier" | "identifier" => {
108                components.push(normalize_cpp_whitespace(node_text(current, source)));
109            }
110            "nested_namespace_specifier" => {
111                for index in (0..current.named_child_count()).rev() {
112                    stack.push(
113                        current
114                            .named_child(index)
115                            .expect("index below the node's own named child count"),
116                    );
117                }
118            }
119            _ => return cpp_raw_namespace_name_components(node, source),
120        }
121    }
122    if components.iter().any(String::is_empty) {
123        return cpp_raw_namespace_name_components(node, source);
124    }
125    components
126}
127
128/// The historical reading of a namespace name node: its whole source text as
129/// one component, with a leading global `::` marker dropped so the caller's
130/// global-scope handling stays the AST boundary rather than a text prefix.
131///
132/// This is the recovery path for source outside the C++ grammar, so the text it
133/// returns can carry a separator that names nothing. react-native-windows
134/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
135/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
136/// trailing separator with no tail. The caller stores this component in
137/// `short_name` and derives the fq by splitting it back apart, so an empty tail
138/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
139/// here, at the one place the malformed text enters, rather than leaving each
140/// consumer to guard (#2353, a variant of #1878).
141fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
142    let start = node
143        .child(0)
144        .filter(|child| !child.is_named() && child.kind() == "::")
145        .map_or(node.start_byte(), |marker| marker.end_byte());
146    let text = normalize_cpp_whitespace(
147        source
148            .get(start..node.end_byte())
149            .expect("namespace name node covers one source range"),
150    );
151    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
152    if text.is_empty() {
153        return Vec::new();
154    }
155    vec![text]
156}
157
158/// Return the named namespace path that structurally encloses `node`.
159///
160/// This intentionally follows namespace AST ancestors rather than inspecting
161/// source text. Anonymous namespaces are not representable in the legacy C++
162/// package field, so a path containing one fails closed.
163fn cpp_lexical_namespace_name<'tree>(
164    node: Node<'tree>,
165    source: &str,
166    ancestry: &ParentIndex<'tree>,
167) -> Option<String> {
168    let mut components = Vec::new();
169    let mut ancestor = ancestry.parent(node);
170    while let Some(current) = ancestor {
171        if current.kind() == "namespace_definition" {
172            let name_node = current.child_by_field_name("name")?;
173            let name = normalize_cpp_whitespace(node_text(name_node, source));
174            if name.is_empty() {
175                return None;
176            }
177            components.push(name);
178        }
179        ancestor = ancestry.parent(current);
180    }
181    if components.is_empty() {
182        return None;
183    }
184    components.reverse();
185    // Same shared empty-component decision as `cpp_push_package`, which splits
186    // this string back apart: an enclosing namespace whose own name node is
187    // malformed contributes a component carrying its own separator (#2353).
188    Some(
189        normalize_joined(
190            &components.join(CPP_PACKAGE_SEPARATOR),
191            CPP_PACKAGE_SEPARATOR,
192        )
193        .into_owned(),
194    )
195}
196
197/// Nested-class `$` join for short names. An anonymous parent class (empty
198/// short_name) contributes no segment: the FqName bridge drops empty
199/// components, so a bare `parent$child` join would desync `short_name` from
200/// the fq and trip the package/short boundary assert in
201/// `CodeUnit::with_signature_and_fq` (#2140).
202fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
203    if parent_short.is_empty() {
204        name.to_string()
205    } else {
206        format!("{parent_short}${name}")
207    }
208}
209
210/// Member `.` join for short names; same anonymous-parent guard as
211/// [`cpp_join_nested_short`] (#2140).
212fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
213    if parent_short.is_empty() {
214        name.to_string()
215    } else {
216        format!("{parent_short}.{name}")
217    }
218}
219
220/// Structural fq for a leaf declaration: the parent unit's fq plus this
221/// declaration's own name as one segment (or the package segments plus the
222/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
223/// chain, so a literal `$` inside a source identifier (Cython template
224/// substitution points, gcc `$`-identifiers) survives instead of corrupting
225/// the chain and tripping the package/short boundary assert (#2140).
226fn cpp_leaf_fq(
227    package_name: &str,
228    parent: Option<&CodeUnit>,
229    name: &str,
230    kind_if_nested: SegmentKind,
231    kind_if_top: SegmentKind,
232) -> FqName {
233    if let Some(parent) = parent {
234        parent
235            .fq()
236            .clone()
237            .with_pushed(cpp_segment(name, kind_if_nested))
238    } else {
239        let mut fq = FqName::new();
240        cpp_push_package(&mut fq, package_name);
241        fq.push(cpp_segment(name, kind_if_top));
242        fq
243    }
244}
245
246/// Structured name for a member unit (function, field, enumerator). The
247/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
248/// the member has an owner, by `.member`; free functions and globals have no
249/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
250/// C++ member names never contain a literal `.`, so the single `.` (if any)
251/// separates the owner chain from the member.
252pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
253    let mut fq = FqName::new();
254    cpp_push_package(&mut fq, package_name);
255    match short_name.rsplit_once('.') {
256        Some((owner_chain, member)) => {
257            cpp_push_type_chain(&mut fq, owner_chain);
258            fq.push(cpp_segment(member, SegmentKind::Member));
259        }
260        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
261    }
262    fq
263}
264
265#[derive(Clone)]
266pub struct ScopeInfo {
267    package_name: String,
268    module: Option<CodeUnit>,
269    class_unit: Option<CodeUnit>,
270    template_signature: Option<String>,
271    template_metadata: Option<CppTemplateMetadata>,
272    declarations_are_fields: bool,
273    recovered_specialization_member_scope: bool,
274    /// Namespace targets of every `using namespace X;` directive lexically
275    /// visible at this point in the file (declaration order), threaded
276    /// forward sibling-by-sibling by the sequential container walk (see
277    /// `CppWork::Siblings`). An out-of-line member definition written as a
278    /// bare `Class::method` at file/namespace scope with no enclosing
279    /// `namespace {}` block (issue #1093, e.g. log4cxx's
280    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
281    /// const { ... }`) has no other structural signal for which namespace
282    /// actually owns `Class`; this is the best-effort candidate list used to
283    /// recover it so the definition's indexed identity matches its header
284    /// declaration's.
285    visible_using_namespaces: Vec<String>,
286}
287
288struct CppContainer<'tree> {
289    node: Node<'tree>,
290    scope: ScopeInfo,
291}
292
293struct CppNodeWork<'tree> {
294    node: Node<'tree>,
295    scope: ScopeInfo,
296}
297
298/// Cursor over one container's remaining named children, processed one at a
299/// time (rather than all at once) so a `using namespace X;` sibling can
300/// update `scope.visible_using_namespaces` for the siblings that follow it,
301/// matching real C++ using-directive semantics. Nested container work is
302/// still pushed and fully drained before the cursor resumes (stack LIFO
303/// order), preserving the original left-to-right visitation order.
304struct CppSiblingsWork<'tree> {
305    children: std::vec::IntoIter<Node<'tree>>,
306    scope: ScopeInfo,
307}
308
309enum CppWork<'tree> {
310    Container(CppContainer<'tree>),
311    Node(CppNodeWork<'tree>),
312    Siblings(CppSiblingsWork<'tree>),
313}
314
315fn class_like_name<'tree>(
316    node: Node<'tree>,
317    source: &str,
318    ancestry: &ParentIndex<'tree>,
319) -> Option<String> {
320    let best = class_like_name_from_children(node, source);
321    if let Some(parent) = ancestry.parent(node)
322        && matches!(
323            parent.kind(),
324            "declaration" | "field_declaration" | "function_definition"
325        )
326        // A class_specifier carrying its own body proves the grammar name is
327        // the real class name: a sibling declarator then declares an object
328        // (`class X {} x;`), never a displaced class name. The gate matters
329        // when the class name is itself an all-caps token (`X`, `API`) --
330        // without it the export-macro re-read below steals the object
331        // declarator's name for the class (#2283). The genuine export-macro
332        // shapes leave the class_specifier bodyless, the same invariant
333        // recover_malformed_exported_multiple_base_class already gates on.
334        && cpp_body_node(node).is_none()
335        && node
336            .child_by_field_name("name")
337            .map(|name_node| {
338                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
339            })
340            .unwrap_or(false)
341        && let Some(recovered) = exported_class_name_from_node(parent, source)
342        && best.as_deref() != Some(recovered.as_str())
343    {
344        return Some(recovered);
345    }
346    best.or_else(|| {
347        node.child_by_field_name("name")
348            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
349            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
350    })
351}
352
353fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
354    let mut grammar_name = None;
355    if let Some(name_node) = node.child_by_field_name("name") {
356        let name = normalize_cpp_whitespace(node_text(name_node, source));
357        if name.is_empty() {
358            return None;
359        }
360        if !cpp_export_macro_token(&name) {
361            return Some(name);
362        }
363        grammar_name = Some(name);
364    }
365
366    let mut best = None;
367    let mut cursor = node.walk();
368    let mut stack = Vec::new();
369    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
370        if matches!(
371            child.kind(),
372            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
373        ) {
374            break;
375        }
376        stack.push(child);
377    }
378
379    while let Some(current) = stack.pop() {
380        if matches!(current.kind(), "type_identifier" | "identifier") {
381            let name = normalize_cpp_whitespace(node_text(current, source));
382            if !name.is_empty() && !cpp_export_macro_token(&name) {
383                best = Some(name);
384            }
385            continue;
386        }
387
388        push_named_children_reversed(current, &mut stack);
389    }
390    best.or(grammar_name)
391}
392
393pub fn cpp_export_macro_token(token: &str) -> bool {
394    token
395        .chars()
396        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
397}
398
399struct RecoveredExportedClass<'tree> {
400    declaration_node: Node<'tree>,
401    name: String,
402    body: Option<Node<'tree>>,
403    raw_supertypes: Option<Vec<String>>,
404    uses_initializer_body: bool,
405    /// Present only for the fragmented multiple-base export shape (issue #938).
406    /// Carries the true class-body byte region -- the members tree-sitter scattered
407    /// out of the recovered node -- so they can be reparsed and re-owned as members
408    /// rather than lost inside the truncated `initializer_list` stand-in.
409    fragmented_body: Option<FragmentedExportBody>,
410}
411
412struct RecoveredFunctionLikeExportClassPair {
413    name: String,
414    range: Range,
415    raw_supertypes: Option<Vec<String>>,
416    fragmented_body: FragmentedExportBody,
417}
418
419struct RecoveredEmbeddedFunctionLikeExportClass {
420    name: String,
421    range: Range,
422    raw_supertypes: Vec<String>,
423    fragmented_body: FragmentedExportBody,
424}
425
426/// The recovered class-body geometry for a fragmented multiple-base export class.
427/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
428/// verbatim for a region reparse (issue #941 machinery) so every recovered member
429/// keeps its exact original byte/line position. `class_range` is the full class
430/// navigation range spanning to the displaced closing brace.
431struct FragmentedExportBody {
432    reparse_start: usize,
433    reparse_end: usize,
434    class_range: Range,
435}
436
437fn recovered_fragmented_export_body(
438    body: Node<'_>,
439    class_range: Range,
440) -> Option<FragmentedExportBody> {
441    let open = body.child(0).filter(|child| child.kind() == "{")?;
442    let close = body
443        .child(body.child_count().saturating_sub(1))
444        .filter(|child| child.kind() == "}" && !child.is_missing());
445    Some(FragmentedExportBody {
446        reparse_start: open.end_byte(),
447        // A zero-width missing `}` contributes no source byte. Keep the whole
448        // body range in that case; subtracting one byte would discard the last
449        // member's semicolon and make the otherwise valid region unsafe to
450        // index. A real close token is excluded by its structured start.
451        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
452        class_range,
453    })
454}
455
456struct DisplacedFragmentNamespaceBoundary<'tree> {
457    class_close: Node<'tree>,
458    class_semicolon: Node<'tree>,
459    namespace_items: Vec<Node<'tree>>,
460}
461
462/// Result of validating a reparsed fragmented class body.  A complete tree can
463/// safely consume the whole region.  A partial tree may contain only the exact
464/// class-named constructor that tree-sitter merged into an access label; its
465/// remaining siblings must stay on the ordinary outer walk.
466enum FragmentedExportMembers {
467    Complete(Tree),
468    ConditionalConstructor(Tree),
469}
470
471#[derive(Clone, Copy)]
472struct DisplacedMacroClassTail {
473    split_index: usize,
474    class_range: Range,
475}
476
477fn recover_exported_class_declaration<'tree>(
478    node: Node<'tree>,
479    source: &str,
480) -> Option<RecoveredExportedClass<'tree>> {
481    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
482        return Some(recovered);
483    }
484
485    let class_node = first_class_like_child(node)?;
486    if let Some(name_node) = class_node.child_by_field_name("name") {
487        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
488        if cpp_export_macro_token(&class_name) {
489            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
490            // Name declarator. Only a bare declarator can be the displaced class name;
491            // wrappers describe an object whose type merely happens to look macro-like.
492            let mut cursor = node.walk();
493            if node
494                .children_by_field_name("declarator", &mut cursor)
495                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
496            {
497                return None;
498            }
499        } else if has_direct_cpp_declarator(node) {
500            return None;
501        }
502    }
503    let name = exported_class_name_from_node(class_node, source)?;
504    Some(RecoveredExportedClass {
505        declaration_node: class_node,
506        name,
507        body: cpp_body_node(class_node),
508        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
509            .then(|| extract_cpp_supertypes(class_node, source)),
510        uses_initializer_body: false,
511        fragmented_body: None,
512    })
513}
514
515fn recover_malformed_exported_base_class<'tree>(
516    node: Node<'tree>,
517    source: &str,
518) -> Option<RecoveredExportedClass<'tree>> {
519    if node.kind() != "declaration" {
520        return None;
521    }
522    let class_node = node.child_by_field_name("type")?;
523    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
524        return None;
525    }
526    let macro_name = class_node
527        .child_by_field_name("name")
528        .and_then(|name| direct_identifier_name(name, source))?;
529    if !cpp_export_macro_token(&macro_name) {
530        return None;
531    }
532
533    let mut named_cursor = node.walk();
534    let mut named = node.named_children(&mut named_cursor);
535    if named
536        .next()
537        .is_none_or(|child| !same_node(child, class_node))
538    {
539        return None;
540    }
541    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
542    if displaced.kind() != "ERROR" {
543        return None;
544    }
545    let name = displaced_exported_class_name(displaced, source)?;
546
547    let remaining = named.collect::<Vec<_>>();
548    let init = *remaining.last()?;
549    if init.kind() != "init_declarator" {
550        return None;
551    }
552    let final_base = init
553        .child_by_field_name("declarator")
554        .and_then(|base| recovered_malformed_base_name(base, source))?;
555    let body = init.child_by_field_name("value")?;
556    // A complete reduction has a real closing brace here. In Chromium's Widget
557    // declaration, tree-sitter instead emits the same direct `}` slot as a
558    // zero-width missing node where the first body macro truncates the prefix.
559    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
560        return None;
561    }
562
563    if remaining[..remaining.len() - 1]
564        .iter()
565        .any(|child| match child.kind() {
566            "qualified_identifier"
567            | "scoped_type_identifier"
568            | "type_identifier"
569            | "identifier" => false,
570            "ERROR" => !is_malformed_inheritance_access(*child, source),
571            _ => true,
572        })
573    {
574        return None;
575    }
576
577    let mut raw_supertypes = Vec::new();
578    for base in &remaining[..remaining.len() - 1] {
579        if base.kind() == "ERROR" {
580            continue;
581        }
582        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
583    }
584    raw_supertypes.push(final_base);
585
586    Some(RecoveredExportedClass {
587        declaration_node: node,
588        name,
589        body: Some(body),
590        raw_supertypes: Some(raw_supertypes),
591        uses_initializer_body: true,
592        fragmented_body: fragmented_export_body_region(node, body, source),
593    })
594}
595
596/// Locate the true class-body region for a fragmented multiple-base export class.
597///
598/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
599/// emits in place of the real class body. Tree-sitter reduces that body in one of
600/// two shapes, both of which lose the members from the recovered node:
601///
602/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
603///   a real closing brace and holds the whole body text inline. The interior between
604///   the braces reparses to the members directly.
605/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
606///   first member with a zero-width MISSING `}`; every later member -- and the real
607///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
608///   siblings. The interior runs from the opening brace to that displaced `}`.
609///
610/// Returns the interior byte range to reparse plus the full class navigation range.
611fn fragmented_export_body_region(
612    node: Node<'_>,
613    body: Node<'_>,
614    source: &str,
615) -> Option<FragmentedExportBody> {
616    let reparse_start = body.start_byte() + 1;
617    let close = direct_close_brace(body)?;
618    if close.end_byte() > close.start_byte() {
619        return Some(FragmentedExportBody {
620            reparse_start,
621            reparse_end: close.start_byte(),
622            class_range: cpp_declaration_range(node),
623        });
624    }
625    // The closing brace was displaced past the recovered node. A balanced nested
626    // class keeps its own braces, so the first lone-`}` sibling is this class's.
627    let mut sibling = node.next_named_sibling();
628    let displaced_close = loop {
629        let Some(current) = sibling else {
630            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
631        };
632        if cpp_is_stray_close_brace(current, source) {
633            break current;
634        }
635        sibling = current.next_named_sibling();
636    };
637    Some(FragmentedExportBody {
638        reparse_start,
639        reparse_end: displaced_close.start_byte(),
640        class_range: Range {
641            start_byte: node.start_byte(),
642            end_byte: displaced_close.end_byte(),
643            start_line: node.start_position().row + 1,
644            end_line: displaced_close.end_position().row + 1,
645        },
646    })
647}
648
649/// Locate the true class-body region for the export-macro class shape that
650/// tree-sitter promotes to a `function_definition`.
651///
652/// In this shape the synthetic function body closes at the first inline
653/// method, while the class's real members continue as root-level siblings until
654/// a stray `}` followed by the displaced class `;`. Reparse the complete
655/// interior so those siblings are visited with the recovered class scope.
656fn fragmented_export_function_body_region(
657    node: Node<'_>,
658    body: Node<'_>,
659    source: &str,
660    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
661) -> Option<FragmentedExportBody> {
662    let reparse_start = body.start_byte().checked_add(1)?;
663    if let Some(boundary) = displaced_namespace {
664        return Some(FragmentedExportBody {
665            reparse_start,
666            reparse_end: boundary.class_close.start_byte(),
667            class_range: Range {
668                start_byte: node.start_byte(),
669                end_byte: boundary.class_semicolon.end_byte(),
670                start_line: node.start_position().row + 1,
671                end_line: boundary.class_semicolon.end_position().row + 1,
672            },
673        });
674    }
675    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
676    let boundary = fragmented_export_sibling_class_boundary(node, source);
677    let boundary_index = boundary.and_then(|boundary| {
678        siblings
679            .iter()
680            .position(|candidate| same_node(*candidate, boundary))
681    });
682    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
683    let mut sibling_index = 0;
684    // A complete recovered class's synthetic wrapper is immediately followed
685    // by its displaced semicolon (comments and a trailing attribute macro --
686    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
687    // may sit between the body and that semicolon). Only scan for a later
688    // stray close when real member siblings intervene; otherwise every earlier
689    // complete class would borrow the next malformed class's close and claim
690    // its members. The trailing-attribute case is the gtest shape: the scan
691    // borrowed a close ~1900 lines later and re-owned a following
692    // `namespace testing { namespace internal {` block as class members,
693    // doubling the package path ("testing::internal::testing::internal") and
694    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
695    // boundary assert (#2297).
696    while let Some(current) = siblings.get(sibling_index).copied() {
697        if current.kind() == "comment" {
698            sibling_index += 1;
699            continue;
700        }
701        if is_trailing_attribute_macro_sibling(current) {
702            sibling_index += 1;
703            continue;
704        }
705        if cpp_is_stray_semicolon(current, source) {
706            return None;
707        }
708        break;
709    }
710    while let Some(current) = siblings.get(sibling_index).copied() {
711        let next = siblings.get(sibling_index + 1).copied();
712        if cpp_is_stray_close_brace(current, source)
713            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
714        {
715            let semicolon = next.expect("checked above");
716            return Some(FragmentedExportBody {
717                reparse_start,
718                reparse_end: current.start_byte(),
719                class_range: Range {
720                    start_byte: node.start_byte(),
721                    end_byte: semicolon.end_byte(),
722                    start_line: node.start_position().row + 1,
723                    end_line: semicolon.end_position().row + 1,
724                },
725            });
726        }
727        // When the final access label keeps the class close in its malformed
728        // declaration body, tree-sitter nests the lone `}` ERROR below the
729        // label instead of exposing it as a direct sibling. Search only the
730        // scattered siblings after the synthetic wrapper. The first such
731        // close is the class terminator because nested class bodies retain
732        // their own balanced class_specifier nodes.
733        if current.start_byte() >= body.end_byte()
734            && let Some(close) = cpp_nested_stray_close_brace(current, source)
735        {
736            return Some(FragmentedExportBody {
737                reparse_start,
738                reparse_end: close.start_byte(),
739                class_range: Range {
740                    start_byte: node.start_byte(),
741                    end_byte: current.end_byte(),
742                    start_line: node.start_position().row + 1,
743                    end_line: current.end_position().row + 1,
744                },
745            });
746        }
747        sibling_index += 1;
748    }
749    boundary.map(|boundary| FragmentedExportBody {
750        reparse_start,
751        reparse_end: boundary.start_byte(),
752        class_range: Range {
753            start_byte: node.start_byte(),
754            end_byte: boundary.start_byte(),
755            start_line: node.start_position().row + 1,
756            end_line: boundary.start_position().row + 1,
757        },
758    })
759}
760
761/// Find a later macro-export class that tree-sitter lifted through an enclosing
762/// preprocessor container. A class that is still a direct sibling can be a
763/// nested member of the current fragmented class, so only a changed parent is
764/// a proven boundary between the two recovered class envelopes.
765fn fragmented_export_sibling_class_boundary<'tree>(
766    node: Node<'tree>,
767    source: &str,
768) -> Option<Node<'tree>> {
769    let node_parent = node.parent()?;
770    cpp_following_named_siblings(node, source, &ParentIndex::unindexed())
771        .into_iter()
772        .find(|candidate| {
773            recover_exported_class_function_definition(*candidate, source).is_some()
774                && candidate
775                    .parent()
776                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
777        })
778}
779
780/// A trailing attribute macro after a recovered class's closing brace, spelled
781/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
782/// bare identifier is never a class member (members need a type), so this
783/// sibling can only be the class's own tail (#2297).
784fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
785    if node.kind() != "expression_statement" {
786        return false;
787    }
788    let mut cursor = node.walk();
789    let mut children = node.named_children(&mut cursor);
790    children
791        .next()
792        .is_some_and(|child| child.kind() == "identifier")
793        && children.next().is_none()
794}
795
796/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
797/// export-class wrapper can place the class close inside an access-label node,
798/// so direct-sibling checks alone miss the boundary.  Walk named CST children
799/// only; the helper does not inspect source text beyond the existing structured
800/// stray-brace predicate.
801fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
802    let mut stack = vec![node];
803    while let Some(current) = stack.pop() {
804        if cpp_is_stray_close_brace(current, source) {
805            return Some(current);
806        }
807        let mut cursor = current.walk();
808        stack.extend(current.named_children(&mut cursor));
809    }
810    None
811}
812
813/// Return named siblings that follow `node`, including siblings that tree-sitter
814/// attached to an enclosing container after malformed recovery split the local
815/// declaration list. Stop at the first structurally visible class close so a
816/// later namespace or exported class cannot supply the recovery boundary.
817///
818/// The climb takes a [`ParentIndex`] because it runs once per visited node and
819/// walks every level to the root: with `Node::parent`, which re-descends from
820/// the root on each call, a translation unit holding thousands of siblings --
821/// generated instruction tables, for instance -- made this the whole cost of
822/// indexing the file. Callers inside the declaration walk pass the walk's
823/// index; the four that ask from outside a traversal pass
824/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
825/// behaviour is unchanged.
826fn cpp_following_named_siblings<'tree>(
827    node: Node<'tree>,
828    source: &str,
829    ancestry: &ParentIndex<'tree>,
830) -> Vec<Node<'tree>> {
831    let mut siblings = Vec::new();
832    let mut anchor = node;
833    while let Some(parent) = ancestry.parent(anchor) {
834        let at_translation_unit = parent.kind() == "translation_unit";
835        let mut sibling = anchor.next_named_sibling();
836        while let Some(current) = sibling {
837            if at_translation_unit
838                && (current.kind() == "namespace_definition"
839                    || (current.kind() == "function_definition"
840                        && first_class_like_child(current).is_some()))
841            {
842                return siblings;
843            }
844            siblings.push(current);
845            if cpp_is_stray_close_brace(current, source) {
846                if let Some(semicolon) = current
847                    .next_named_sibling()
848                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
849                {
850                    siblings.push(semicolon);
851                }
852                return siblings;
853            }
854            if current.start_byte() >= node.end_byte()
855                && matches!(current.kind(), "ERROR" | "labeled_statement")
856                && cpp_nested_stray_close_brace(current, source).is_some()
857            {
858                return siblings;
859            }
860            sibling = current.next_named_sibling();
861        }
862        anchor = parent;
863    }
864    siblings
865}
866
867fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
868    if node.start_byte() >= class_end {
869        return false;
870    }
871    node.end_byte() <= class_end
872        || cpp_nested_stray_close_brace(node, source)
873            .is_some_and(|close| close.start_byte() == class_end)
874}
875
876/// A class whose head tree-sitter kept in one malformed container while the
877/// members after the first and the closing `};` scattered across that
878/// container's following siblings.
879struct FragmentedClassRecovery<'tree> {
880    declaration_node: Node<'tree>,
881    name: String,
882    raw_supertypes: Vec<String>,
883    body: FragmentedExportBody,
884}
885
886/// Recover a class whose opening prefix is retained in one ERROR node while one
887/// or more nested class closes and the outer close are displaced to sibling
888/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
889/// tokens establish nesting depth and the displaced close nodes terminate it.
890///
891/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
892/// `class` keyword as the container's first token. A function-like export macro
893/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
894/// body-less `class_specifier` named after the macro followed by the
895/// invocation's `(`, arguments and `)`, with the real class name and the base
896/// clause left as bare tokens after it (#2924).
897fn fragmented_class_body<'tree>(
898    node: Node<'tree>,
899    source: &str,
900) -> Option<FragmentedClassRecovery<'tree>> {
901    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
902        return Some(recovered);
903    }
904    let supported_container = node.kind() == "ERROR"
905        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
906    if !supported_container {
907        return None;
908    }
909    let mut cursor = node.walk();
910    let children = node.children(&mut cursor).collect::<Vec<_>>();
911    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
912        return Some(recovered);
913    }
914    let keyword = children.first()?;
915    if !matches!(keyword.kind(), "class" | "struct" | "union") {
916        return None;
917    }
918    let name_node = children
919        .iter()
920        .copied()
921        .skip(1)
922        .find(|child| child.is_named())?;
923    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
924        return None;
925    }
926    let name = normalize_cpp_whitespace(node_text(name_node, source));
927    if name.is_empty() || cpp_export_macro_token(&name) {
928        return None;
929    }
930    let open_index = children.iter().position(|child| child.kind() == "{")?;
931    Some(FragmentedClassRecovery {
932        declaration_node: node,
933        name,
934        raw_supertypes: extract_cpp_supertypes(node, source),
935        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
936    })
937}
938
939/// The fragmented body of a function-like export-macro class head that
940/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
941/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
942/// public virtual EC_PublicKey {`, #2924).
943///
944/// The class name is read positionally by [`recovered_export_head_name`] -- the
945/// last identifier before the head ends at `final`, at the base clause `:`, or
946/// at the body `{` -- so no macro spelling is interpreted, and a base clause
947/// spelled with `virtual`, with `final`, or with qualified base names needs no
948/// arm of its own.
949fn fragmented_export_macro_class_body<'tree>(
950    node: Node<'tree>,
951    children: &[Node<'tree>],
952    source: &str,
953) -> Option<FragmentedClassRecovery<'tree>> {
954    let class_node = *children.first()?;
955    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
956        return None;
957    }
958    // The identifier tree-sitter took for the class name is the export macro, a
959    // function-like invocation when the `(` of its argument list follows it
960    // directly. Spelling plays no part (#2557).
961    class_node
962        .child_by_field_name("name")
963        .and_then(|name| direct_identifier_name(name, source))?;
964    let invocation = *children.get(1)?;
965    if invocation.is_named() || invocation.kind() != "(" {
966        return None;
967    }
968    let open_index = children
969        .iter()
970        .position(|child| !child.is_named() && child.kind() == "{")?;
971    let open = children[open_index];
972    let name_node = recovered_export_head_name(node, open, source)?;
973    let name = normalize_cpp_whitespace(node_text(name_node, source));
974    if name.is_empty() || cpp_export_macro_token(&name) {
975        return None;
976    }
977    Some(FragmentedClassRecovery {
978        declaration_node: node,
979        name,
980        raw_supertypes: recovered_export_head_bases(
981            node,
982            name_node.end_byte(),
983            open.start_byte(),
984            source,
985        ),
986        body: fragmented_displaced_class_body(node, children, open_index, source)
987            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
988    })
989}
990
991/// The class body a fragmented head opened at `children[open_index]` but could
992/// not close: tree-sitter scattered the remaining members and the closing `};`
993/// across the container's following siblings. The direct class tokens after the
994/// opening brace say how many closes the nested classes consume before this
995/// class's own, and the displaced `}`/`;` sibling pair terminates it.
996fn fragmented_displaced_class_body(
997    node: Node<'_>,
998    children: &[Node<'_>],
999    open_index: usize,
1000    source: &str,
1001) -> Option<FragmentedExportBody> {
1002    let open = children[open_index];
1003    let nested_class_opens = children[open_index + 1..]
1004        .iter()
1005        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1006        .count();
1007    let mut closes_remaining = 1 + nested_class_opens;
1008    let mut sibling = node.next_named_sibling();
1009    while let Some(candidate) = sibling {
1010        let next = candidate.next_named_sibling();
1011        if cpp_is_stray_close_brace(candidate, source) {
1012            closes_remaining -= 1;
1013            if closes_remaining == 0 {
1014                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1015                if open.end_byte() >= candidate.start_byte() {
1016                    return None;
1017                }
1018                return Some(FragmentedExportBody {
1019                    reparse_start: open.end_byte(),
1020                    reparse_end: candidate.start_byte(),
1021                    class_range: Range {
1022                        start_byte: node.start_byte(),
1023                        end_byte: semicolon.end_byte(),
1024                        start_line: node.start_position().row + 1,
1025                        end_line: semicolon.end_position().row + 1,
1026                    },
1027                });
1028            }
1029        }
1030        sibling = next;
1031    }
1032    None
1033}
1034
1035/// The body of a fragmented export-macro class head whose close tree-sitter
1036/// spent on the enclosing container instead: Botan's
1037/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1038/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1039/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1040/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1041/// (#2924).
1042fn fragmented_container_close_class_body(
1043    node: Node<'_>,
1044    open: Node<'_>,
1045    source: &str,
1046) -> Option<FragmentedExportBody> {
1047    let parent = node.parent()?;
1048    if !matches!(
1049        parent.kind(),
1050        "declaration_list" | "field_declaration_list" | "compound_statement"
1051    ) {
1052        return None;
1053    }
1054    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1055    // The container's close is this class's close only when the body's own
1056    // brace balance says so. Without that cross-check -- the one issue #1524
1057    // already uses on a mis-closed wrapper body -- a class that really does end
1058    // earlier would claim every later container-level declaration as a member.
1059    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1060        || open.end_byte() >= close.start_byte()
1061    {
1062        return None;
1063    }
1064    Some(FragmentedExportBody {
1065        reparse_start: open.end_byte(),
1066        reparse_end: close.start_byte(),
1067        class_range: Range {
1068            start_byte: node.start_byte(),
1069            end_byte: close.end_byte(),
1070            start_line: node.start_position().row + 1,
1071            end_line: close.end_position().row + 1,
1072        },
1073    })
1074}
1075
1076pub(crate) fn recovered_fragmented_class_has_body(
1077    node: Node<'_>,
1078    source: &str,
1079    expected_name: &str,
1080    expected_range: &Range,
1081) -> bool {
1082    fragmented_class_body(node, source).is_some_and(|recovered| {
1083        recovered.name == expected_name
1084            && recovered.body.class_range.start_byte == expected_range.start_byte
1085            && recovered.body.class_range.end_byte == expected_range.end_byte
1086    })
1087}
1088
1089/// Recover a plain class whose parser-visible body ends inside a malformed
1090/// inline member. Tree-sitter then attaches either the next real member
1091/// declarator or the unfinished `else` branch directly to the outer function
1092/// definition and leaves the class's actual `};` among later siblings. Those
1093/// structured continuations and the close/semicolon siblings establish the
1094/// complete body envelope without interpreting source text.
1095fn fragmented_plain_class_declaration_body<'tree>(
1096    node: Node<'tree>,
1097    source: &str,
1098) -> Option<FragmentedClassRecovery<'tree>> {
1099    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1100        return None;
1101    }
1102    let class_node = node.child_by_field_name("type")?;
1103    if !matches!(
1104        class_node.kind(),
1105        "class_specifier" | "struct_specifier" | "union_specifier"
1106    ) {
1107        return None;
1108    }
1109    let name_node = class_node.child_by_field_name("name")?;
1110    let name = normalize_cpp_whitespace(node_text(name_node, source));
1111    if name.is_empty() || cpp_export_macro_token(&name) {
1112        return None;
1113    }
1114    let body = cpp_body_node(class_node)?;
1115    if body.kind() != "field_declaration_list" {
1116        return None;
1117    }
1118    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1119        if declarator.start_byte() < class_node.end_byte() {
1120            return None;
1121        }
1122        let mut cursor = node.walk();
1123        node.named_children(&mut cursor).any(|child| {
1124            if child.kind() != "ERROR"
1125                || child.start_byte() < class_node.end_byte()
1126                || child.end_byte() > declarator.start_byte()
1127            {
1128                return false;
1129            }
1130            let mut cursor = child.walk();
1131            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1132            let Some((return_type, attributes)) = components.split_last() else {
1133                return false;
1134            };
1135            matches!(
1136                return_type.kind(),
1137                "identifier"
1138                    | "type_identifier"
1139                    | "primitive_type"
1140                    | "decltype"
1141                    | "placeholder_type_specifier"
1142            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1143                && attributes.iter().all(|attribute| {
1144                    matches!(attribute.kind(), "identifier" | "type_identifier")
1145                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1146                            *attribute, source,
1147                        )))
1148                })
1149        })
1150    } else {
1151        let mut cursor = node.walk();
1152        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1153        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1154            if same_node(*candidate_class, class_node)
1155                && continuation.kind() == "identifier"
1156                && node_text(*continuation, source) == "else"
1157                && continuation_body.kind() == "compound_statement"
1158                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1159                && continuation_body
1160                    .child(continuation_body.child_count().saturating_sub(1))
1161                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1162    };
1163    if !displaced_member {
1164        return None;
1165    }
1166    let open = body
1167        .children(&mut body.walk())
1168        .find(|child| child.kind() == "{")?;
1169    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1170    let ordinary_boundary =
1171        siblings
1172            .iter()
1173            .copied()
1174            .enumerate()
1175            .find_map(|(close_index, close)| {
1176                cpp_is_stray_close_brace(close, source)
1177                    .then(|| {
1178                        siblings
1179                            .get(close_index + 1)
1180                            .copied()
1181                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1182                            .map(|semicolon| (close, semicolon))
1183                    })
1184                    .flatten()
1185            });
1186    let (close, semicolon) =
1187        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1188            (boundary.class_close, boundary.class_semicolon)
1189        } else {
1190            ordinary_boundary?
1191        };
1192    if open.end_byte() >= close.start_byte() {
1193        return None;
1194    }
1195    Some(FragmentedClassRecovery {
1196        declaration_node: class_node,
1197        name,
1198        raw_supertypes: extract_cpp_supertypes(class_node, source),
1199        body: FragmentedExportBody {
1200            reparse_start: open.end_byte(),
1201            reparse_end: close.start_byte(),
1202            class_range: Range {
1203                start_byte: class_node.start_byte(),
1204                end_byte: semicolon.end_byte(),
1205                start_line: class_node.start_position().row + 1,
1206                end_line: semicolon.end_position().row + 1,
1207            },
1208        },
1209    })
1210}
1211
1212fn displaced_export_function_namespace_shape<'tree>(
1213    declaration: Node<'tree>,
1214    source: &str,
1215) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1216    let mut nested = Vec::new();
1217    for index in (0..declaration.named_child_count()).rev() {
1218        nested.push(declaration.named_child(index)?);
1219    }
1220    while let Some(current) = nested.pop() {
1221        // A recovered export class nested in this class can consume the first
1222        // parser-visible namespace close itself. In that shape the existing
1223        // later-class boundary logic already distinguishes the nested and
1224        // namespace-sibling owners; do not mistake the nested close for this
1225        // class's terminator.
1226        if recover_exported_class_function_definition(current, source).is_some() {
1227            return None;
1228        }
1229        for index in (0..current.named_child_count()).rev() {
1230            nested.push(current.named_child(index)?);
1231        }
1232    }
1233    let mut same_envelope_sibling = declaration.next_named_sibling();
1234    while let Some(current) = same_envelope_sibling {
1235        if recover_exported_class_function_definition(current, source).is_some() {
1236            return None;
1237        }
1238        same_envelope_sibling = current.next_named_sibling();
1239    }
1240    let declaration_list = declaration.parent()?;
1241    if declaration_list.kind() != "declaration_list" {
1242        return None;
1243    }
1244    let namespace = declaration_list.parent()?;
1245    if namespace.kind() != "namespace_definition"
1246        || namespace.child_by_field_name("body") != Some(declaration_list)
1247    {
1248        return None;
1249    }
1250    let class_close = direct_close_brace(declaration_list)?;
1251    let trailing_semicolon = namespace.next_named_sibling()?;
1252    if trailing_semicolon.kind() != "expression_statement"
1253        || trailing_semicolon.named_child_count() != 0
1254    {
1255        return None;
1256    }
1257    // A chain of malformed export classes can consume one parser-visible
1258    // namespace close per class. Walk through the enclosing sibling levels so
1259    // the later real namespace close remains the structural boundary; a
1260    // direct next-sibling walk stops at the first collapsed namespace and
1261    // incorrectly makes its intervening items members of this class.
1262    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1263    let trailing_index = siblings
1264        .iter()
1265        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1266    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1267        recover_exported_class_function_definition(*candidate, source).is_some()
1268    }) {
1269        // Consecutive recovered classes already have an exact sibling-class
1270        // boundary. Preserve that established path, including nested export
1271        // classes, instead of interpreting the first class close as a
1272        // collapsed namespace boundary.
1273        return None;
1274    }
1275    let mut namespace_items = Vec::new();
1276    let mut nested_fragment_end = 0;
1277    for current in siblings.into_iter().skip(trailing_index + 1) {
1278        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1279        {
1280            return Some(DisplacedFragmentNamespaceBoundary {
1281                class_close,
1282                class_semicolon: trailing_semicolon,
1283                namespace_items,
1284            });
1285        }
1286        if current.start_byte() >= nested_fragment_end
1287            && let Some(recovered) = fragmented_class_body(current, source)
1288        {
1289            nested_fragment_end = recovered.body.class_range.end_byte;
1290        } else if current.start_byte() >= nested_fragment_end
1291            && recover_exported_class_function_definition(current, source).is_some()
1292            && let Some(body) = cpp_body_node(current)
1293            && let Some(fragmented) =
1294                fragmented_export_function_body_region(current, body, source, None)
1295        {
1296            nested_fragment_end = fragmented.class_range.end_byte;
1297        }
1298        namespace_items.push(current);
1299    }
1300    None
1301}
1302
1303fn displaced_fragment_namespace_boundary<'tree>(
1304    declaration: Node<'tree>,
1305    body: Node<'tree>,
1306    source: &str,
1307) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1308    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1309    let reparse_start = body.start_byte() + 1;
1310    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1311    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1312}
1313
1314/// Recover the class/namespace brace geometry for a declaration whose class
1315/// close tree-sitter consumed as the enclosing namespace close. This proof is
1316/// independent of whether every member in the class body can be reparsed: the
1317/// ordinary-tree fallback can still re-own bounded sibling declarations when
1318/// an unknown macro makes the complete body reparse unsafe.
1319fn displaced_fragment_namespace_geometry<'tree>(
1320    declaration: Node<'tree>,
1321    source: &str,
1322) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1323    // A templated class's malformed function wrapper remains beneath the
1324    // template node even though its later members have escaped to the
1325    // enclosing declaration list. Lift only that exact declaration child.
1326    let envelope = declaration
1327        .parent()
1328        .filter(|parent| {
1329            parent.kind() == "template_declaration"
1330                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1331        })
1332        .unwrap_or(declaration);
1333    let declaration_list = envelope.parent()?;
1334    if declaration_list.kind() != "declaration_list" {
1335        return None;
1336    }
1337    let namespace = declaration_list.parent()?;
1338    if namespace.kind() != "namespace_definition"
1339        || namespace.child_by_field_name("body") != Some(declaration_list)
1340    {
1341        return None;
1342    }
1343    let class_close = direct_close_brace(declaration_list)?;
1344    let trailing_semicolon = namespace.next_named_sibling()?;
1345    if trailing_semicolon.kind() != "expression_statement"
1346        || trailing_semicolon.named_child_count() != 0
1347    {
1348        return None;
1349    }
1350    let mut namespace_items = Vec::new();
1351    let mut sibling = trailing_semicolon.next_named_sibling();
1352    let mut nested_fragment_end = 0;
1353    loop {
1354        let current = sibling?;
1355        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1356        {
1357            break;
1358        }
1359        if current.start_byte() >= nested_fragment_end
1360            && let Some(recovered) = fragmented_class_body(current, source)
1361        {
1362            nested_fragment_end = recovered.body.class_range.end_byte;
1363        }
1364        namespace_items.push(current);
1365        sibling = current.next_named_sibling();
1366    }
1367    Some(DisplacedFragmentNamespaceBoundary {
1368        class_close,
1369        class_semicolon: trailing_semicolon,
1370        namespace_items,
1371    })
1372}
1373
1374/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1375fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1376    (0..node.child_count())
1377        .filter_map(|index| node.child(index))
1378        .find(|child| !child.is_named() && child.kind() == "}")
1379}
1380
1381/// A displaced lone closing brace: the class close that the fragmented multiple-base
1382/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1383fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1384    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1385}
1386
1387/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1388/// text while skipping line/block comments and string/char literals. The
1389/// exported-class recovery needs this when tree-sitter's bogus
1390/// `function_definition` body runs past the class's true closing brace and
1391/// swallows following siblings (issue #1524): the grammar tree carries no
1392/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1393/// close is located textually. Returns `None` when the text is unbalanced or
1394/// contains a construct the scanner deliberately does not interpret (raw
1395/// strings) -- callers treat that as "cannot partition" and keep the
1396/// un-split recovery.
1397fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1398    let bytes = source.as_bytes();
1399    if bytes.get(open_byte) != Some(&b'{') {
1400        return None;
1401    }
1402    let mut depth = 0usize;
1403    let mut i = open_byte;
1404    while i < bytes.len() {
1405        match bytes[i] {
1406            b'{' => depth += 1,
1407            b'}' => {
1408                depth = depth.checked_sub(1)?;
1409                if depth == 0 {
1410                    return Some(i);
1411                }
1412            }
1413            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1414                while i < bytes.len() && bytes[i] != b'\n' {
1415                    i += 1;
1416                }
1417                continue;
1418            }
1419            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1420                i += 2;
1421                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1422                    i += 1;
1423                }
1424                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1425                continue;
1426            }
1427            quote @ (b'"' | b'\'') => {
1428                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1429                // bail out rather than mis-count.
1430                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1431                    return None;
1432                }
1433                i += 1;
1434                while i < bytes.len() && bytes[i] != quote {
1435                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1436                }
1437                if i >= bytes.len() {
1438                    return None;
1439                }
1440            }
1441            _ => {}
1442        }
1443        i += 1;
1444    }
1445    None
1446}
1447
1448fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1449    let mut name = None;
1450    let mut colon_count = 0;
1451    let mut access_count = 0;
1452    for index in 0..node.child_count() {
1453        let child = node.child(index)?;
1454        match child.kind() {
1455            "identifier" | "type_identifier" if child.is_named() => {
1456                if name.is_some() {
1457                    return None;
1458                }
1459                let candidate = normalize_cpp_whitespace(node_text(child, source));
1460                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1461                    return None;
1462                }
1463                name = Some(candidate);
1464            }
1465            "template_function" | "template_type" if child.is_named() => {
1466                if name.is_some() {
1467                    return None;
1468                }
1469                let candidate = child
1470                    .child_by_field_name("name")
1471                    .and_then(|name| direct_identifier_name(name, source))?;
1472                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1473                    return None;
1474                }
1475                name = Some(candidate);
1476            }
1477            ":" if !child.is_named() => colon_count += 1,
1478            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1479            _ => return None,
1480        }
1481    }
1482    (colon_count == 1 && access_count == 1)
1483        .then_some(name)
1484        .flatten()
1485}
1486
1487fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1488    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1489        return false;
1490    }
1491    node.named_child(0)
1492        .and_then(|child| direct_identifier_name(child, source))
1493        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1494}
1495
1496fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1497    (0..node.child_count()).any(|index| {
1498        node.child(index)
1499            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1500    })
1501}
1502
1503fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1504    match node.kind() {
1505        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1506            recovered_base_atom(node, source)
1507        }
1508        "template_type" | "template_function" => node
1509            .child_by_field_name("name")
1510            .and_then(|name| recovered_malformed_base_name(name, source)),
1511        "ERROR" => None,
1512        "qualified_identifier" | "scoped_type_identifier" => {
1513            let suffix = node
1514                .child_by_field_name("name")
1515                .and_then(|name| recovered_malformed_base_name(name, source))?;
1516            let scope = node
1517                .child_by_field_name("scope")
1518                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1519            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1520                malformed_qualified_prefix(node, source)?
1521            } else {
1522                if malformed_qualified_prefix(node, source).is_some() {
1523                    return None;
1524                }
1525                scope
1526            };
1527            Some(format!("{prefix}::{suffix}"))
1528        }
1529        _ => None,
1530    }
1531}
1532
1533fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1534    if !matches!(
1535        node.kind(),
1536        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1537    ) {
1538        return None;
1539    }
1540    let name = normalize_cpp_whitespace(node_text(node, source));
1541    (!name.is_empty()).then_some(name)
1542}
1543
1544fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1545    let mut prefix = None;
1546    let mut cursor = node.walk();
1547    for error in node
1548        .named_children(&mut cursor)
1549        .filter(|child| child.kind() == "ERROR")
1550    {
1551        if prefix.is_some() {
1552            return None;
1553        }
1554        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1555        // the qualified name's scope and leaves `virtual` beside the real
1556        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1557        // component of the base's name (#2924).
1558        let mut error_cursor = error.walk();
1559        let atoms = error
1560            .named_children(&mut error_cursor)
1561            .map(|child| recovered_base_atom(child, source))
1562            .collect::<Option<Vec<_>>>()?;
1563        let [atom] = atoms
1564            .iter()
1565            .filter(|atom| atom.as_str() != "virtual")
1566            .collect::<Vec<_>>()[..]
1567        else {
1568            return None;
1569        };
1570        prefix = Some(atom.clone());
1571    }
1572    prefix
1573}
1574
1575/// One declaration an attribute-like macro invocation swallowed into a
1576/// declaration-scope `ERROR`, with the byte range that spells it.
1577/// What [`stranded_declaration_run`] read out of one node.
1578struct StrandedRun<'tree> {
1579    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1580    /// Whether every part of the node read as part of a declaration. False when
1581    /// a part the reader does not understand ended it early, or when the last
1582    /// parts were types with no declarator after them. Callers that index what
1583    /// was found keep the declarations either way; a caller deciding whether a
1584    /// whole region is safe to index requires this.
1585    complete: bool,
1586}
1587
1588struct MacroWrappedDeclaration<'tree> {
1589    declarator: Node<'tree>,
1590    range: Range,
1591    /// Whether the recovered declaration spells `static`. The envelope hides
1592    /// that keyword from the ordinary linkage reader, which looks for it among
1593    /// a declaration node's own children, and internal linkage is what decides
1594    /// whether a header declaration and a body in another file are one symbol.
1595    is_static: bool,
1596}
1597
1598/// Whether `node` stands where declarations live: directly in the translation
1599/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1600/// stands in one of those.
1601///
1602/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1603/// where it stands. The case that matters is a whole translation unit the
1604/// parser could not close: whisper.cpp's `include/whisper.h` opens
1605/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1606/// so the braces never balance in the token stream tree-sitter sees, the file's
1607/// own root node is an `ERROR`, and every top-level declaration in the file is
1608/// a child of it (#3094). Without this the macro recoveries below read nothing
1609/// in such a file, because none of their envelopes is in a declaration scope
1610/// the parser managed to build.
1611fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1612    declaration_scope_container(node, ancestry).is_some()
1613}
1614
1615/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1616/// end is where a parse failure that starts at `node` can still be doing
1617/// damage: a declaration scope has no synchronization point of its own, so the
1618/// parser carries such a failure to the scope's close.
1619fn declaration_scope_container<'tree>(
1620    node: Node<'tree>,
1621    ancestry: &ParentIndex<'tree>,
1622) -> Option<Node<'tree>> {
1623    let mut parent = ancestry.parent(node)?;
1624    loop {
1625        match parent.kind() {
1626            "translation_unit" => return Some(parent),
1627            "declaration_list" => {
1628                return ancestry
1629                    .parent(parent)
1630                    .is_some_and(|grandparent| {
1631                        matches!(
1632                            grandparent.kind(),
1633                            "namespace_definition" | "linkage_specification"
1634                        )
1635                    })
1636                    .then_some(parent);
1637            }
1638            "ERROR" => match ancestry.parent(parent) {
1639                Some(grandparent) => parent = grandparent,
1640                // A root `ERROR` is the translation unit the parser could not
1641                // build, so its children stand at file scope.
1642                None => return Some(parent),
1643            },
1644            _ => return None,
1645        }
1646    }
1647}
1648
1649/// Whether `node` is an `ERROR` the parser produced where declarations live.
1650fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1651    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1652}
1653
1654/// Whether `node` can only be part of what precedes a declarator -- a type, a
1655/// specifier, or a word of the attribute macro's own text that the lexer left
1656/// as a bare identifier -- so a run of these followed by a declarator is one
1657/// declaration the parser failed to group.
1658fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1659    matches!(
1660        node.kind(),
1661        "identifier"
1662            | "type_identifier"
1663            | "primitive_type"
1664            | "sized_type_specifier"
1665            | "struct_specifier"
1666            | "union_specifier"
1667            | "enum_specifier"
1668            | "type_qualifier"
1669            | "storage_class_specifier"
1670            | "explicit_function_specifier"
1671            | "virtual_function_specifier"
1672            | "qualified_identifier"
1673            | "template_type"
1674            | "dependent_type"
1675            | "placeholder_type_specifier"
1676    )
1677}
1678
1679/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1680/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1681/// words the lexer reports as bare identifiers. Anything else in such an
1682/// `ERROR` stops the recovery, so a macro argument that carries structure this
1683/// recovery does not understand is never guessed at.
1684fn is_macro_argument_error(node: Node<'_>) -> bool {
1685    if node.kind() != "ERROR" {
1686        return false;
1687    }
1688    let mut cursor = node.walk();
1689    node.named_children(&mut cursor).all(|child| {
1690        matches!(
1691            child.kind(),
1692            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1693        )
1694    })
1695}
1696
1697/// The end of a recovered declaration: the declarator's end, extended across
1698/// the `;` the grammar left beside it inside the envelope.
1699///
1700/// The envelope's own end is the hard boundary. The parser hands the last
1701/// declaration's `;` to the sibling statement it recovered with, outside the
1702/// envelope, and a range that reached it would no longer lie inside one node --
1703/// which is how every reader (including the resolver's climb from a range to
1704/// the node that declares it) finds a recovered declaration again.
1705fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1706    declarator
1707        .next_sibling()
1708        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1709        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1710}
1711
1712/// The declarations `node` holds after an attribute-like macro cost the parser
1713/// their grouping, in source order.
1714///
1715/// The parser packs the parts into whichever slots it has left. In whisper's
1716/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1717/// field takes the export macro, the real return type and the *next*
1718/// declaration's declarator both end up inside one sibling `ERROR`, and the
1719/// `declarator` field takes whatever declaration the recovery reached last. In
1720/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1721/// arrive as bare identifiers and the attributed member and the member after it
1722/// share one declarator node.
1723///
1724/// Both are the same failure, so both get the same reading: flatten the node's
1725/// parts -- splicing each nested `ERROR`'s own children in place, since an
1726/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1727/// spells, a run of type-and-specifier tokens followed by a declarator, over
1728/// and over.
1729///
1730/// Fails closed. A part that is neither a declarator nor something that can
1731/// only precede one ends the recovery there, and the declarations before it are
1732/// kept.
1733fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1734    let mut parts = Vec::new();
1735    let mut cursor = node.walk();
1736    for child in node.named_children(&mut cursor) {
1737        if child.kind() == "ERROR" {
1738            let mut error_cursor = child.walk();
1739            parts.extend(child.named_children(&mut error_cursor));
1740        } else {
1741            parts.push(child);
1742        }
1743    }
1744
1745    let mut declarations = Vec::new();
1746    let mut start = None;
1747    let mut is_static = false;
1748    let mut complete = true;
1749    for part in parts {
1750        if part.kind() == "comment" {
1751            continue;
1752        }
1753        if let Some(declarator) = extract_function_declarator(part) {
1754            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1755            declarations.push(MacroWrappedDeclaration {
1756                declarator,
1757                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1758                is_static,
1759            });
1760            is_static = false;
1761            continue;
1762        }
1763        if !is_recovered_declaration_type_part(part) {
1764            complete = false;
1765            break;
1766        }
1767        is_static |= part.kind() == "storage_class_specifier"
1768            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1769        start.get_or_insert(part.start_byte());
1770    }
1771    StrandedRun {
1772        declarations,
1773        complete: complete && start.is_none(),
1774    }
1775}
1776
1777/// The declarations an attribute-like macro invocation swallowed into a
1778/// declaration-scope `ERROR`, in source order.
1779///
1780/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1781/// declaration in a macro call:
1782///
1783/// ```text
1784/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1785///                  struct llama_model * model,
1786///           struct llama_context_params   params),
1787///         "use llama_init_from_model instead");
1788/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1789/// ```
1790///
1791/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1792/// holding the macro name, the wrapped declaration as a
1793/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1794/// following declaration as a further `parameter_declaration` until it
1795/// recovers. That is what removed `llama_tokenize` from the index and left the
1796/// seven-argument call in `talk-llama.cpp` with only that file's own
1797/// three-parameter `static` overload to choose from (#2552, and the
1798/// `LLAMA_API` half of #2551).
1799///
1800/// Every part of every swallowed declaration is still a real node; only their
1801/// grouping is lost. This rebuilds the grouping and reads the nodes.
1802fn macro_wrapped_declarations<'tree>(
1803    envelope: Node<'tree>,
1804    source: &str,
1805    ancestry: &ParentIndex<'tree>,
1806) -> Vec<MacroWrappedDeclaration<'tree>> {
1807    let mut declarations = Vec::new();
1808    if !is_declaration_scope_error(envelope, ancestry) {
1809        return declarations;
1810    }
1811    let mut cursor = envelope.walk();
1812    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1813    let [macro_name, arguments @ ..] = children.as_slice() else {
1814        return declarations;
1815    };
1816    if macro_name.kind() != "identifier" {
1817        return declarations;
1818    }
1819    let mut wrapped_declaration_seen = false;
1820    for argument in arguments {
1821        match argument.kind() {
1822            "comment" => {}
1823            "parameter_declaration" => {
1824                let recovered = stranded_declaration_run(*argument, source).declarations;
1825                if recovered.is_empty() {
1826                    break;
1827                }
1828                wrapped_declaration_seen = true;
1829                declarations.extend(recovered);
1830            }
1831            // The hint string, and only that: an argument the recovery cannot
1832            // read as a declaration is admitted before the wrapped declaration
1833            // is found, so a macro whose first argument is not a declaration
1834            // recovers nothing.
1835            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1836            _ => break,
1837        }
1838    }
1839    declarations
1840}
1841
1842/// What one macro invocation swallowed when it collapsed a whole run of
1843/// declarations.
1844struct CollapsedMacroDeclarationRun {
1845    /// The byte just past the `;` that closes the invocation, which is where
1846    /// the declarations it swallowed begin.
1847    invocation_end: usize,
1848    /// The byte this recovery owns to: the close of the enclosing declaration
1849    /// scope when the invocation swallowed the declarations written after it,
1850    /// and the invocation's own end when the parser instead spread the
1851    /// invocation itself across the container's children and swallowed nothing.
1852    region_end: usize,
1853}
1854
1855/// The tokens one collapsed macro invocation spans, in source order: the leaves
1856/// of `node`, then the leaves of the siblings written after it. Comments and
1857/// the parser's own MISSING tokens are skipped; neither is in the source the
1858/// invocation is written in.
1859///
1860/// The parser puts the parts of a failed invocation wherever it has room. It
1861/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
1862/// `expression_statement` it recovered with; it spreads them flat over the
1863/// container's children; it packs the tail of the file into one of the
1864/// arguments. The grouping therefore says nothing, but the token order still
1865/// spells the invocation, which is what this reads.
1866struct MacroInvocationTokens<'tree> {
1867    stack: Vec<Node<'tree>>,
1868    /// The last sibling handed to `stack`, and the point the next one is asked
1869    /// for. Held as the node rather than as its already-computed successor so
1870    /// the successor is asked for only when the walk actually runs off the end
1871    /// of what it has: `Node::next_sibling` recovers the parent first, which
1872    /// re-descends from the root, so it costs the node's position in the tree.
1873    /// Nearly every node this iterator is built for is rejected on its first
1874    /// token, before any sibling is needed, and computing one eagerly made that
1875    /// rejection cost a walk of the whole container (capstone's generated
1876    /// instruction tables, t19).
1877    frontier: Option<Node<'tree>>,
1878}
1879
1880impl<'tree> MacroInvocationTokens<'tree> {
1881    fn new(node: Node<'tree>) -> Self {
1882        Self {
1883            stack: vec![node],
1884            frontier: Some(node),
1885        }
1886    }
1887}
1888
1889impl<'tree> Iterator for MacroInvocationTokens<'tree> {
1890    type Item = Node<'tree>;
1891
1892    fn next(&mut self) -> Option<Node<'tree>> {
1893        loop {
1894            let Some(node) = self.stack.pop() else {
1895                let sibling = self.frontier?.next_sibling()?;
1896                self.frontier = Some(sibling);
1897                self.stack.push(sibling);
1898                continue;
1899            };
1900            if node.child_count() == 0 {
1901                if node.kind() == "comment" || node.is_missing() {
1902                    continue;
1903                }
1904                return Some(node);
1905            }
1906            let mut cursor = node.walk();
1907            let children = node.children(&mut cursor).collect::<Vec<_>>();
1908            self.stack.extend(children.into_iter().rev());
1909        }
1910    }
1911}
1912
1913/// The macro invocation that begins at `node` when the parser could not group
1914/// it, or `None` when `node` does not begin one.
1915///
1916/// whisper.cpp's bundled `llama.h` writes
1917///
1918/// ```text
1919/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
1920///                          const char * path_model,
1921///           struct llama_model_params   params),
1922///         "use llama_model_load_from_file instead");
1923/// ```
1924///
1925/// and its own `include/whisper.h` writes
1926///
1927/// ```text
1928/// WHISPER_DEPRECATED(
1929///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
1930///     "use whisper_init_from_file_with_params instead"
1931/// );
1932/// ```
1933///
1934/// tree-sitter cannot know either name is a macro, so it reads the name and its
1935/// `(` as a function declarator whose close it never finds, and carries that
1936/// failure forward over the declarations written after it. In `llama.h` that
1937/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
1938/// (#2551). In `whisper.h` it instead flattens the invocations over the
1939/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
1940/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
1941/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
1942/// `function_definition` when a brace block falls in a swallowed tail, because
1943/// the parser borrows it for the body the bogus definition needs, and an
1944/// `ERROR` or a bare token when none does. None of that says anything about the
1945/// construct, so the node's kind is not the criterion; the token order is.
1946///
1947/// The invocation ends at the `)` that closes its own `(`, immediately followed
1948/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
1949/// balanced by the `(` beside it, and a macro argument list carries no `;` of
1950/// its own, so a `;` reached before the close means this is some other
1951/// construct -- a macro-defined function body, say -- and the ordinary readers
1952/// keep the node.
1953///
1954/// Fails closed everywhere else too. The head must be an export-macro-shaped
1955/// token followed by `(`, the node must stand where declarations live, and an
1956/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
1957/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
1958/// nothing past it -- is left to that reader.
1959fn collapsed_macro_declaration_run<'tree>(
1960    node: Node<'tree>,
1961    source: &str,
1962    ancestry: &ParentIndex<'tree>,
1963) -> Option<CollapsedMacroDeclarationRun> {
1964    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
1965    let mut tokens = MacroInvocationTokens::new(node);
1966    let name = tokens.next()?;
1967    if !matches!(name.kind(), "identifier" | "type_identifier")
1968        || !cpp_export_macro_token(node_text(name, source))
1969    {
1970        return None;
1971    }
1972    if tokens.next()?.kind() != "(" {
1973        return None;
1974    }
1975    let mut depth = 1usize;
1976    let invocation_end = loop {
1977        let token = tokens.next()?;
1978        match token.kind() {
1979            "(" => depth += 1,
1980            ";" => return None,
1981            ")" => {
1982                depth -= 1;
1983                if depth == 0 {
1984                    let semicolon = tokens.next()?;
1985                    if semicolon.kind() != ";" {
1986                        return None;
1987                    }
1988                    break semicolon.end_byte();
1989                }
1990            }
1991            _ => {}
1992        }
1993    };
1994    // An invocation that reaches the end of its own declaration scope swallowed
1995    // nothing: there is nothing after it left to recover, and the ordinary
1996    // readers have the invocation itself. This is also what stops the scan
1997    // below from re-entering itself, since the region it reparses for one
1998    // invocation ends exactly at that invocation's `;`.
1999    if invocation_end >= container_end {
2000        return None;
2001    }
2002    // Where the damage ends. A node that reaches past its own invocation packed
2003    // real declarations into the invocation's argument list, so the parser was
2004    // still failing when it got there and everything to the close of the scope
2005    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2006    // line 455 with it, and every declaration from there to the end of the file
2007    // is shredded. A node that stops at or before the invocation's `;` holds
2008    // nothing but the invocation, whose own parts the parser spread over the
2009    // siblings after it.
2010    let region_end = if node.end_byte() > invocation_end {
2011        container_end
2012    } else {
2013        invocation_end
2014    };
2015    Some(CollapsedMacroDeclarationRun {
2016        invocation_end,
2017        region_end,
2018    })
2019}
2020
2021/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2022/// ordinary class body, and the members it swallowed.
2023///
2024/// Botan deprecates members that way:
2025///
2026/// ```text
2027/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2028/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2029/// ```
2030///
2031/// The parser reads the macro name as the member's type and its argument list
2032/// as a parenthesized declarator, which then swallows the attributed member
2033/// *and* the member written after it: a `field_declaration` whose `type` is a
2034/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2035/// opening with an `ERROR` whose first part is a bare identifier -- the first
2036/// word of the string, which the lexer could not keep together.
2037///
2038/// The macro's spelling is not the criterion; that structural shape is. The
2039/// returned declarations are in source order, the first being the attributed
2040/// member and the rest the members the declarator swallowed after it.
2041fn string_attribute_macro_member_declarators<'tree>(
2042    field: Node<'tree>,
2043    source: &str,
2044) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2045    if field.kind() != "field_declaration"
2046        || field
2047            .child_by_field_name("type")
2048            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2049    {
2050        return None;
2051    }
2052    let declarator = field.child_by_field_name("declarator")?;
2053    if declarator.kind() != "parenthesized_declarator" {
2054        return None;
2055    }
2056    let opening = declarator.named_child(0)?;
2057    if opening.kind() != "ERROR"
2058        || opening
2059            .named_child(0)
2060            .is_none_or(|word| word.kind() != "identifier")
2061    {
2062        return None;
2063    }
2064    let declarations = stranded_declaration_run(declarator, source).declarations;
2065    (!declarations.is_empty()).then_some(declarations)
2066}
2067
2068/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2069/// export-macro class body leaves in front of the members that macro decorated.
2070///
2071/// The reparse reads the class body as statements, so the attribute becomes a
2072/// call statement of its own -- with the `;` the grammar had to invent -- and
2073/// the members after it are stranded in the `ERROR` that follows.
2074fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2075    let Some(call) = (node.kind() == "expression_statement")
2076        .then(|| node.named_child(0))
2077        .flatten()
2078        .filter(|child| child.kind() == "call_expression")
2079    else {
2080        return false;
2081    };
2082    call.child_by_field_name("function")
2083        .is_some_and(|function| function.kind() == "identifier")
2084        && call
2085            .child_by_field_name("arguments")
2086            .is_some_and(|arguments| {
2087                let mut cursor = arguments.walk();
2088                arguments.named_child_count() > 0
2089                    && arguments
2090                        .named_children(&mut cursor)
2091                        .all(|argument| argument.kind() == "string_literal")
2092            })
2093}
2094
2095/// The start byte of the call the region reparse left where an access-labeled
2096/// constructor was written.
2097///
2098/// A constructor is a member only inside a class body. The export-macro class
2099/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2100/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2101/// comma-expression under `private:`, with no declarator left in the tree. It
2102/// is still spelled in the source, though, starting at the one call under the
2103/// label whose callee is the class's own name. Botan's private six-parameter
2104/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2105/// call is an ambiguity this declines, and so is a reparse from that byte that
2106/// does not yield exactly one constructor declarator there.
2107fn cpp_access_label_constructor_call_start(
2108    node: Node<'_>,
2109    class_name: &str,
2110    source: &str,
2111) -> Option<usize> {
2112    if node.kind() != "labeled_statement" {
2113        return None;
2114    }
2115    let label = node.named_child(0)?;
2116    if label.kind() != "statement_identifier"
2117        || !matches!(
2118            node_text(label, source).trim(),
2119            "public" | "private" | "protected"
2120        )
2121    {
2122        return None;
2123    }
2124    let mut starts = Vec::new();
2125    let mut stack = vec![node];
2126    while let Some(current) = stack.pop() {
2127        if current.kind() == "call_expression"
2128            && current
2129                .child_by_field_name("function")
2130                .is_some_and(|function| {
2131                    function.kind() == "identifier"
2132                        && node_text(function, source).trim() == class_name
2133                })
2134        {
2135            starts.push(current.start_byte());
2136        }
2137        let mut cursor = current.walk();
2138        stack.extend(current.named_children(&mut cursor));
2139    }
2140    let [start] = starts.as_slice() else {
2141        return None;
2142    };
2143    Some(*start)
2144}
2145
2146/// The `function_definition` that gives `declarator` a body, following only the
2147/// declarator chain, so a recovered callable knows whether it is a declaration
2148/// or a definition without anyone having to say.
2149fn cpp_declarator_function_definition<'tree>(
2150    declarator: Node<'tree>,
2151    ancestry: &ParentIndex<'tree>,
2152) -> Option<Node<'tree>> {
2153    let mut current = declarator;
2154    while let Some(parent) = ancestry.parent(current) {
2155        match parent.kind() {
2156            "function_definition" if parent.child_by_field_name("body").is_some() => {
2157                return Some(parent);
2158            }
2159            "pointer_declarator"
2160            | "reference_declarator"
2161            | "parenthesized_declarator"
2162            | "array_declarator" => current = parent,
2163            _ => return None,
2164        }
2165    }
2166    None
2167}
2168
2169/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2170/// declaration walk still reaches, so a recovery that runs ahead of that walk
2171/// would name what it finds without the namespace.
2172fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2173    let mut current = node;
2174    while let Some(parent) = ancestry.parent(current) {
2175        if parent.kind() == "namespace_definition"
2176            && parent.child_by_field_name("body") == Some(current)
2177        {
2178            return true;
2179        }
2180        current = parent;
2181    }
2182    false
2183}
2184
2185/// Whether the source a recovered callable's byte range spells is a definition
2186/// (a declarator with a body) rather than a declaration.
2187///
2188/// A callable recovered from a mangled region owns no node of its own in the
2189/// file's tree, so the occurrence-role scan that climbs from the range to the
2190/// node containing it lands on whatever container the parser left and answers
2191/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2192/// constructor a declaration and left its call site with nothing to navigate to
2193/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2194/// extraction used, and read the answer from the one declaration it spells.
2195///
2196/// `None` when the range is not one recovered declaration on its own, which is
2197/// the case for every ordinary declaration, so callers keep their own answer.
2198pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2199    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2200    let root = tree.root_node();
2201    let mut cursor = root.walk();
2202    let items = root
2203        .named_children(&mut cursor)
2204        .filter(|child| child.kind() != "comment")
2205        .collect::<Vec<_>>();
2206    let [item] = items.as_slice() else {
2207        return None;
2208    };
2209    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2210        return None;
2211    }
2212    match item.kind() {
2213        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2214        "declaration" | "field_declaration" => Some(false),
2215        _ => None,
2216    }
2217}
2218
2219/// Whether `node` is an envelope an attribute-like macro invocation left where
2220/// declarations were written: the declaration-scope `ERROR`
2221/// [`macro_wrapped_declarations`] reads, or the collapsed run
2222/// [`collapsed_macro_declaration_run`] reads.
2223///
2224/// Extraction and resolution must read one definition of this shape. The
2225/// resolver climbs from a declaration's recorded byte range to the node that
2226/// declares it, and a recovered declaration's range lies inside one of these
2227/// envelopes rather than inside a `declaration` node, so the climb stops here
2228/// (the same role `is_recovered_exported_class_container` plays for a recovered
2229/// class).
2230pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2231    let ancestry = ParentIndex::unindexed();
2232    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2233        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2234}
2235
2236fn recover_exported_class_function_definition<'tree>(
2237    node: Node<'tree>,
2238    source: &str,
2239) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2240    if node.kind() != "function_definition" {
2241        return None;
2242    }
2243    if let Some(prefix) = node.prev_named_sibling()
2244        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2245        && recovered.range.end_byte == node.end_byte()
2246    {
2247        return Some((node, recovered.name, recovered.raw_supertypes));
2248    }
2249    let type_node = node.child_by_field_name("type")?;
2250    let declarator = node.child_by_field_name("declarator")?;
2251
2252    if matches!(
2253        type_node.kind(),
2254        "class_specifier" | "struct_specifier" | "union_specifier"
2255    ) {
2256        let type_name = type_node
2257            .child_by_field_name("name")
2258            .and_then(|name| direct_identifier_name(name, source));
2259        let exported_macro_type = type_name
2260            .as_ref()
2261            .is_some_and(|name| cpp_export_macro_token(name));
2262        if exported_macro_type {
2263            let mut cursor = node.walk();
2264            let errors_before_declarator = node
2265                .named_children(&mut cursor)
2266                .filter(|child| {
2267                    child.kind() == "ERROR"
2268                        && child.start_byte() >= type_node.end_byte()
2269                        && child.end_byte() <= declarator.start_byte()
2270                })
2271                .collect::<Vec<_>>();
2272            if let Some(name) = errors_before_declarator
2273                .iter()
2274                .find_map(|error| displaced_exported_class_name(*error, source))
2275            {
2276                let raw_supertypes = errors_before_declarator
2277                    .iter()
2278                    .any(|error| malformed_inheritance_syntax(*error))
2279                    .then(|| recovered_malformed_base_name(declarator, source))
2280                    .flatten()
2281                    .map(|base| vec![base]);
2282                return Some((node, name, raw_supertypes));
2283            }
2284            if errors_before_declarator
2285                .iter()
2286                .any(|error| malformed_inheritance_syntax(*error))
2287            {
2288                return None;
2289            }
2290        }
2291        if !exported_macro_type
2292            && let Some(name) = type_name
2293            && !cpp_export_macro_token(&name)
2294            && let Some(base) =
2295                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2296        {
2297            return Some((node, name, Some(vec![base])));
2298        }
2299        if let Some(name) = direct_identifier_name(declarator, source)
2300            && exported_macro_type
2301            && !cpp_export_macro_token(&name)
2302        {
2303            let raw_supertypes = exported_macro_type
2304                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2305                .flatten()
2306                .map(|base| vec![base]);
2307            return Some((node, name, raw_supertypes));
2308        }
2309        if declarator.kind() == "parenthesized_declarator"
2310            && type_node
2311                .child_by_field_name("name")
2312                .and_then(|name| direct_identifier_name(name, source))
2313                .is_some_and(|name| cpp_export_macro_token(&name))
2314        {
2315            if let Some((name, base)) =
2316                recovered_function_like_export_class_owner(declarator, source)
2317            {
2318                return Some((node, name, Some(vec![base])));
2319            }
2320            let body_start = node
2321                .child_by_field_name("body")
2322                .map(|body| body.start_byte())
2323                .unwrap_or(node.end_byte());
2324            let mut cursor = node.walk();
2325            if let Some(name) = node
2326                .named_children(&mut cursor)
2327                .filter(|child| {
2328                    child.kind() == "ERROR"
2329                        && child.start_byte() >= declarator.end_byte()
2330                        && child.end_byte() <= body_start
2331                })
2332                .find_map(|error| declarator_name_from_node(error, source))
2333            {
2334                return Some((node, name, None));
2335            }
2336        }
2337    }
2338
2339    let declarator_text = direct_identifier_name(declarator, source)?;
2340    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2341        return None;
2342    }
2343    class_identifier_before_body(node, source).map(|name| (node, name, None))
2344}
2345
2346fn recovered_function_like_export_class_owner(
2347    declarator: Node<'_>,
2348    source: &str,
2349) -> Option<(String, String)> {
2350    if declarator.kind() != "parenthesized_declarator" {
2351        return None;
2352    }
2353    let mut cursor = declarator.walk();
2354    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2355    let [prefix, base] = children.as_slice() else {
2356        return None;
2357    };
2358    if prefix.kind() != "ERROR"
2359        || !matches!(
2360            base.kind(),
2361            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2362        )
2363    {
2364        return None;
2365    }
2366    let mut identifiers = Vec::new();
2367    let mut prefix_cursor = prefix.walk();
2368    for child in prefix.named_children(&mut prefix_cursor) {
2369        match child.kind() {
2370            "number_literal" | "string_literal" | "char_literal" => {}
2371            "identifier" | "type_identifier" => {
2372                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2373            }
2374            _ => return None,
2375        }
2376    }
2377    let name = match identifiers.as_slice() {
2378        [name] => name.clone(),
2379        [name, final_token] if final_token == "final" => name.clone(),
2380        _ => return None,
2381    };
2382    if name.is_empty() || cpp_export_macro_token(&name) {
2383        return None;
2384    }
2385    let base = recovered_malformed_base_name(*base, source)?;
2386    Some((name, base))
2387}
2388
2389/// Collect the base names tree-sitter scattered across the recovered head of a
2390/// function-like export-macro class. `skip` names the structural children that
2391/// are not bases, such as the class name and the body. A base arrives either as
2392/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2393/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2394/// the access specifiers in the same position as the bases, so drop them.
2395/// The bases a recovered function-like export-macro class head names between
2396/// `after` (the end of the class name, or of the access specifier that stands
2397/// in for it) and `before` (the start of the body, or of the `init_declarator`
2398/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2399/// fragments, and one fragment can also hold the class name and `final`
2400/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2401/// than by the fragment that holds it.
2402fn recovered_export_head_bases(
2403    node: Node<'_>,
2404    after: usize,
2405    before: usize,
2406    source: &str,
2407) -> Vec<String> {
2408    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2409    let mut bases = Vec::new();
2410    let mut cursor = node.walk();
2411    for child in node.named_children(&mut cursor) {
2412        if child.kind() == "ERROR" {
2413            let mut error_cursor = child.walk();
2414            bases.extend(
2415                child
2416                    .named_children(&mut error_cursor)
2417                    .filter(within)
2418                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2419            );
2420        } else if within(&child)
2421            && let Some(base) = recovered_malformed_base_name(child, source)
2422        {
2423            bases.push(base);
2424        }
2425    }
2426    bases.retain(|base| {
2427        !matches!(
2428            base.as_str(),
2429            "final" | "public" | "protected" | "private" | "virtual"
2430        )
2431    });
2432    bases
2433}
2434
2435/// Whether `token` is the `final` that closes a recovered class head. The
2436/// grammar keeps it as the `final` keyword when it recognised the head
2437/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2438/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2439fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2440    if token.is_named() {
2441        token.kind() == "identifier" && node_text(token, source) == "final"
2442    } else {
2443        token.kind() == "final"
2444    }
2445}
2446
2447/// The class name of a recovered function-like export-macro class head, read by
2448/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2449/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2450/// carries the body. The head's tokens, in source order with `ERROR` fragments
2451/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2452/// identifier before the head ends, and the head ends at `final`, at the base
2453/// clause `:`, or at `tail`. Every identifier before the name is decoration
2454/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2455/// (`X509_CA`) is a class name like any other.
2456///
2457/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2458/// rule, not a token of the source, so it is never the name.
2459fn recovered_export_head_name<'tree>(
2460    node: Node<'tree>,
2461    tail: Node<'tree>,
2462    source: &str,
2463) -> Option<Node<'tree>> {
2464    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2465}
2466
2467/// The class name of a head whose tokens straddle the recovered pair: the
2468/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2469/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2470/// The positional rule is the same, read over the two token runs in source
2471/// order, so a name in either node wins by position rather than by which node
2472/// holds it.
2473fn recovered_export_pair_head_name<'tree>(
2474    prefix: Node<'tree>,
2475    sibling: Node<'tree>,
2476    tail: Node<'tree>,
2477    source: &str,
2478) -> Option<Node<'tree>> {
2479    let mut tokens = export_head_tokens(prefix, None);
2480    tokens.extend(export_head_tokens(sibling, Some(tail)));
2481    export_head_name_from_tokens(&tokens, source)
2482}
2483
2484/// One node's head tokens in source order, with `ERROR` fragments spliced in
2485/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2486/// before `tail` when the caller names one.
2487fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2488    let mut tokens = Vec::new();
2489    let mut cursor = node.walk();
2490    for child in node.children(&mut cursor) {
2491        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2492            break;
2493        }
2494        if child.kind() == "ERROR" {
2495            let mut fragment_cursor = child.walk();
2496            tokens.extend(child.children(&mut fragment_cursor));
2497        } else {
2498            tokens.push(child);
2499        }
2500    }
2501    tokens
2502}
2503
2504/// The last identifier before the head ends, per the positional rule.
2505fn export_head_name_from_tokens<'tree>(
2506    tokens: &[Node<'tree>],
2507    source: &str,
2508) -> Option<Node<'tree>> {
2509    let mut name = None;
2510    for token in tokens.iter().copied() {
2511        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2512        {
2513            break;
2514        }
2515        if token.is_named()
2516            && !token.is_missing()
2517            && matches!(
2518                token.kind(),
2519                "identifier" | "type_identifier" | "field_identifier"
2520            )
2521        {
2522            name = Some(token);
2523        }
2524    }
2525    name
2526}
2527
2528/// The `init_declarator` whose `value` carries the class body when the grammar
2529/// keeps a function-like export-macro class head as one `declaration`.
2530fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2531    let mut cursor = declaration.walk();
2532    declaration
2533        .named_children(&mut cursor)
2534        .find(|child| child.kind() == "init_declarator")
2535}
2536
2537/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2538/// of a function-like export-macro class head. The grammar splits a base list
2539/// across bare declarator fields, `ERROR` fragments, and one trailing
2540/// `init_declarator` whose `value` is the class body. `head_end` is where the
2541/// class identity ends and the bases can begin: the end of the access specifier
2542/// when the class name went to a statement label, and the end of the class name
2543/// itself otherwise.
2544fn recovered_export_declaration_tail<'tree>(
2545    declaration: Node<'tree>,
2546    head_end: usize,
2547    source: &str,
2548) -> Option<(Vec<String>, Node<'tree>)> {
2549    let init = recovered_export_init_declarator(declaration)?;
2550    let body = init.child_by_field_name("value")?;
2551    if body.kind() != "initializer_list" {
2552        return None;
2553    }
2554    let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2555    bases.extend(recovered_export_head_bases(
2556        init,
2557        init.start_byte(),
2558        body.start_byte(),
2559        source,
2560    ));
2561    Some((bases, body))
2562}
2563
2564/// Whether `node` is the head fragment tree-sitter leaves where
2565/// `class MACRO(args)` was written: the class keyword and the export macro's
2566/// function-like invocation in that order, with nothing else between them.
2567/// Spelling plays no part -- the invocation's `(` following the macro
2568/// identifier directly is what makes it function-like (#2557).
2569///
2570/// Two reductions produce the head. On its own the grammar keeps a body-less
2571/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2572/// with the invocation's `(`, arguments and `)` beside it. After object-like
2573/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2574/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2575/// `declaration` whose class keyword is demoted to a bare `identifier` and
2576/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2577fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2578    match node.kind() {
2579        "ERROR" => {
2580            let Some(class_node) = first_class_like_child(node) else {
2581                return false;
2582            };
2583            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2584                return false;
2585            }
2586            if class_node
2587                .child_by_field_name("name")
2588                .and_then(|name| direct_identifier_name(name, source))
2589                .is_none()
2590            {
2591                return false;
2592            }
2593            class_node
2594                .next_sibling()
2595                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2596        }
2597        "declaration" => {
2598            let mut cursor = node.walk();
2599            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2600            // The class keyword lost its own node: it survives as a bare
2601            // identifier spelled `class`, `struct` or `union`, which is never a
2602            // declarator or a type in well-formed code. It can sit inside an
2603            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2604            // a grouping failure rather than a construct, so read through it.
2605            let Some(keyword) = children
2606                .iter()
2607                .copied()
2608                .flat_map(|child| {
2609                    let mut cursor = child.walk();
2610                    if child.kind() == "ERROR" {
2611                        child.named_children(&mut cursor).collect::<Vec<_>>()
2612                    } else {
2613                        vec![child]
2614                    }
2615                })
2616                .find(|child| {
2617                    child.kind() == "identifier"
2618                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2619                })
2620            else {
2621                return false;
2622            };
2623            children.last().is_some_and(|init| {
2624                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2625                    let mut cursor = init.walk();
2626                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2627                    matches!(parts.as_slice(), [macro_name, arguments]
2628                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2629                            && arguments.kind() == "argument_list")
2630                }
2631            })
2632        }
2633        _ => false,
2634    }
2635}
2636
2637fn recover_function_like_export_class_pair(
2638    node: Node<'_>,
2639    source: &str,
2640) -> Option<RecoveredFunctionLikeExportClassPair> {
2641    if !is_function_like_export_class_head(node, source) {
2642        return None;
2643    }
2644    let sibling = node.next_named_sibling()?;
2645    let (name, raw_supertypes, body) = match sibling.kind() {
2646        // At translation-unit scope tree-sitter can leave the malformed class
2647        // head in one ERROR node and parse its body as the adjacent compound
2648        // statement. The head still carries the export invocation, displaced
2649        // class name, and optional `final` token in source order, so recover
2650        // the name by the same positional rule as the other shapes.
2651        "compound_statement" => (
2652            recovered_export_pair_head_name(node, sibling, sibling, source)
2653                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2654            None,
2655            sibling,
2656        ),
2657        "expression_statement" => {
2658            let compound = sibling.named_child(0)?;
2659            if compound.kind() != "compound_literal_expression" {
2660                return None;
2661            }
2662            let body = compound.child_by_field_name("value")?;
2663            if body.kind() != "initializer_list" {
2664                return None;
2665            }
2666            (
2667                compound
2668                    .child_by_field_name("type")
2669                    .and_then(|name| direct_identifier_name(name, source))?,
2670                None,
2671                body,
2672            )
2673        }
2674        "labeled_statement" => {
2675            let label = sibling.child_by_field_name("label")?;
2676            if label.kind() != "statement_identifier" {
2677                return None;
2678            }
2679            let name = normalize_cpp_whitespace(node_text(label, source));
2680            let declaration = sibling
2681                .named_children(&mut sibling.walk())
2682                .find(|child| child.kind() == "declaration")?;
2683            let access = declaration.child_by_field_name("type")?;
2684            if !matches!(
2685                node_text(access, source),
2686                "public" | "protected" | "private"
2687            ) {
2688                return None;
2689            }
2690            let (bases, body) =
2691                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2692            (name, (!bases.is_empty()).then_some(bases), body)
2693        }
2694        // `class MACRO(2, 0) Name final { ... };`,
2695        // `class MACRO(2, 0) Name final : public Base { ... };`, and
2696        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
2697        // spread over the `type` field, the declarator field, and `ERROR`
2698        // fragments beside them; the name is the last one before the head ends.
2699        "function_definition" => {
2700            let body = sibling.child_by_field_name("body")?;
2701            if body.kind() != "compound_statement" {
2702                return None;
2703            }
2704            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2705            let bases = recovered_export_head_bases(
2706                sibling,
2707                name_node.end_byte(),
2708                body.start_byte(),
2709                source,
2710            );
2711            (
2712                normalize_cpp_whitespace(node_text(name_node, source)),
2713                (!bases.is_empty()).then_some(bases),
2714                body,
2715            )
2716        }
2717        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2718        // comma-separated base list makes the grammar keep the whole tail as one
2719        // declaration whose trailing `init_declarator` carries the body.
2720        "declaration" => {
2721            let init = recovered_export_init_declarator(sibling)?;
2722            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2723            let (bases, body) =
2724                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2725            (
2726                normalize_cpp_whitespace(node_text(name_node, source)),
2727                (!bases.is_empty()).then_some(bases),
2728                body,
2729            )
2730        }
2731        _ => return None,
2732    };
2733    debug_assert!(
2734        !name.is_empty(),
2735        "a recovered class head names its class by an identifier token"
2736    );
2737    let range = Range {
2738        start_byte: node.start_byte(),
2739        end_byte: sibling.end_byte(),
2740        start_line: node.start_position().row + 1,
2741        end_line: sibling.end_position().row + 1,
2742    };
2743    Some(RecoveredFunctionLikeExportClassPair {
2744        name,
2745        raw_supertypes,
2746        range,
2747        fragmented_body: recovered_fragmented_export_body(body, range)?,
2748    })
2749}
2750
2751/// Recover a function-like export-macro class that tree-sitter embedded in a
2752/// larger error after an earlier malformed class body. The grammar still
2753/// preserves every part of the class head: the `class` token, export macro
2754/// identifier and argument list, displaced class identifier, access specifier,
2755/// base field, and initializer-list-shaped body. Match only that complete
2756/// structured sequence and keep each recovered class's exact byte envelope.
2757fn recover_embedded_function_like_export_classes(
2758    node: Node<'_>,
2759    source: &str,
2760) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2761    if !node.is_error() {
2762        return Vec::new();
2763    }
2764
2765    let mut nodes = Vec::new();
2766    let mut stack = vec![node];
2767    while let Some(current) = stack.pop() {
2768        nodes.push(current);
2769        push_children_reversed(current, &mut stack);
2770    }
2771    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2772
2773    // The scans below ask the same handful of kind questions once per node of
2774    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
2775    let language = node.language();
2776    let class_kind = NodeKindIds::new(&language, "class");
2777    let identifier_kinds = [
2778        NodeKindIds::new(&language, "identifier"),
2779        NodeKindIds::new(&language, "type_identifier"),
2780        NodeKindIds::new(&language, "field_identifier"),
2781    ];
2782    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2783    let colon_kind = NodeKindIds::new(&language, ":");
2784    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2785
2786    let mut recovered = Vec::new();
2787    for class_token in nodes
2788        .iter()
2789        .copied()
2790        .filter(|child| !child.is_named() && class_kind.matches(*child))
2791    {
2792        let row = class_token.start_position().row;
2793        // The head after the `class` token reads `MACRO(args) macros... name
2794        // [final] : bases`. The macro is the first identifier, a function-like
2795        // invocation when its argument list is the next thing after it, and the
2796        // name is the last identifier before the head ends at `final` or at the
2797        // base clause `:`. Spelling plays no part (#2557).
2798        let is_identifier = |candidate: &Node<'_>| {
2799            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2800        };
2801        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2802            candidate.start_byte() >= class_token.end_byte()
2803                && candidate.start_position().row == row
2804                && is_identifier(candidate)
2805        }) else {
2806            continue;
2807        };
2808        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2809            argument_list_kind.matches(*candidate)
2810                && candidate.start_byte() >= macro_name.end_byte()
2811                && candidate.start_position().row == row
2812        }) else {
2813            continue;
2814        };
2815        if nodes.iter().any(|candidate| {
2816            is_identifier(candidate)
2817                && candidate.start_byte() >= macro_name.end_byte()
2818                && candidate.end_byte() <= arguments.start_byte()
2819        }) {
2820            continue;
2821        }
2822        let Some(head_end) = nodes.iter().copied().find(|candidate| {
2823            candidate.start_byte() >= arguments.end_byte()
2824                && (recovered_export_head_final(*candidate, source)
2825                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
2826        }) else {
2827            continue;
2828        };
2829        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2830            is_identifier(candidate)
2831                && candidate.start_byte() >= arguments.end_byte()
2832                && candidate.end_byte() <= head_end.start_byte()
2833                && candidate.start_position().row == row
2834        }) else {
2835            continue;
2836        };
2837        let name = normalize_cpp_whitespace(node_text(name_node, source));
2838        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2839            field_initializer_kind.matches(*candidate)
2840                && candidate.start_byte() >= name_node.end_byte()
2841                && candidate
2842                    .child_by_field_name("field")
2843                    .or_else(|| candidate.named_child(0))
2844                    .is_some()
2845                && candidate
2846                    .child_by_field_name("value")
2847                    .or_else(|| {
2848                        let mut cursor = candidate.walk();
2849                        candidate
2850                            .named_children(&mut cursor)
2851                            .find(|child| child.kind() == "initializer_list")
2852                    })
2853                    .is_some_and(|value| value.kind() == "initializer_list")
2854        }) else {
2855            continue;
2856        };
2857        let has_access = nodes.iter().copied().any(|candidate| {
2858            candidate.start_byte() >= name_node.end_byte()
2859                && candidate.end_byte() <= base_initializer.start_byte()
2860                && matches!(
2861                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2862                    "public" | "protected" | "private"
2863                )
2864        });
2865        if !has_access {
2866            continue;
2867        }
2868        let Some(base_node) = base_initializer
2869            .child_by_field_name("field")
2870            .or_else(|| base_initializer.named_child(0))
2871        else {
2872            continue;
2873        };
2874        let Some(base) = recovered_malformed_base_name(base_node, source) else {
2875            continue;
2876        };
2877        let body = base_initializer
2878            .child_by_field_name("value")
2879            .or_else(|| {
2880                let mut cursor = base_initializer.walk();
2881                base_initializer
2882                    .named_children(&mut cursor)
2883                    .find(|child| child.kind() == "initializer_list")
2884            })
2885            .expect("initializer-list value checked above");
2886        let range = Range {
2887            start_byte: class_token.start_byte(),
2888            end_byte: body.end_byte(),
2889            start_line: class_token.start_position().row + 1,
2890            end_line: body.end_position().row + 1,
2891        };
2892        if recovered
2893            .iter()
2894            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
2895                existing.name == name && existing.range == range
2896            })
2897        {
2898            continue;
2899        }
2900        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
2901            name,
2902            range,
2903            raw_supertypes: vec![base],
2904            fragmented_body: match recovered_fragmented_export_body(body, range) {
2905                Some(fragmented) => fragmented,
2906                None => continue,
2907            },
2908        });
2909    }
2910    recovered
2911}
2912
2913fn lifted_function_like_export_class_namespace<'tree>(
2914    node: Node<'tree>,
2915    source: &str,
2916    ancestry: &ParentIndex<'tree>,
2917) -> Option<String> {
2918    // A long malformed body can embed the next exported class several levels
2919    // below a bogus top-level function_definition. Compare namespace evidence
2920    // against that top-level envelope, not only the recovered ERROR's direct
2921    // parent. The source tree still proves the same boundary: one earlier
2922    // malformed namespace and one later standalone closing brace.
2923    let mut anchor = node;
2924    let parent = loop {
2925        let parent = ancestry.parent(anchor)?;
2926        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
2927            break parent;
2928        }
2929        anchor = parent;
2930    };
2931    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
2932        sibling.start_byte() > anchor.end_byte()
2933            && sibling.kind() == "ERROR"
2934            && sibling.named_child_count() == 0
2935            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
2936    });
2937    if !has_later_close {
2938        return None;
2939    }
2940    let candidates = parent
2941        .named_children(&mut parent.walk())
2942        .filter(|sibling| {
2943            sibling.kind() == "namespace_definition"
2944                && sibling.has_error()
2945                && sibling.end_byte() < anchor.start_byte()
2946        })
2947        .filter_map(|namespace| {
2948            namespace
2949                .child_by_field_name("name")
2950                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
2951                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
2952        })
2953        .collect::<Vec<_>>();
2954    let [namespace] = candidates.as_slice() else {
2955        return None;
2956    };
2957    Some(namespace.clone())
2958}
2959
2960pub(crate) fn recovered_function_like_export_class_pair_has_body(
2961    node: Node<'_>,
2962    source: &str,
2963    identifier: &str,
2964    range: &Range,
2965) -> bool {
2966    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
2967        recovered.name == identifier
2968            && recovered.range.start_byte == range.start_byte
2969            && recovered.range.end_byte == range.end_byte
2970    })
2971}
2972
2973/// One file's embedded export-macro class recovery, resolved once and keyed by
2974/// the `ERROR` node that mints each set.
2975///
2976/// [`recover_embedded_function_like_export_classes`] collects and sorts an
2977/// `ERROR` node's whole subtree, and the declaration-strength question asks it
2978/// once per class-like unit in the file. On a translation unit the parser could
2979/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
2980/// node spans most of the file -- that is one full subtree pass per class,
2981/// quadratic in the file's size, and it was 78% of that file's inverse scan
2982/// (#1496).
2983///
2984/// Keyed by byte span rather than node identity, so one analyzer generation's
2985/// re-parses of the same content share the index. A nested `ERROR` that shares
2986/// its parent's span recovers the same classes: the only node the parent adds
2987/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
2988#[derive(Default)]
2989pub struct CppRecoveredExportClassIndex {
2990    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
2991}
2992
2993impl CppRecoveredExportClassIndex {
2994    pub fn build(root: Node<'_>, source: &str) -> Self {
2995        let mut by_error_node: HashMap<
2996            (usize, usize),
2997            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
2998        > = HashMap::default();
2999        // One cursor for the whole-file walk, and the error question asked by
3000        // symbol rather than by `kind()` string (#3097).
3001        let mut cursor = root.walk();
3002        let mut stack = vec![root];
3003        while let Some(node) = stack.pop() {
3004            if node.is_error() {
3005                let recovered = recover_embedded_function_like_export_classes(node, source);
3006                if !recovered.is_empty() {
3007                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3008                }
3009            }
3010            stack.extend(node.named_children(&mut cursor));
3011        }
3012        Self { by_error_node }
3013    }
3014
3015    /// The bytes this index holds, for the analyzer cache's weight.
3016    pub fn approximate_size(&self) -> usize {
3017        self.by_error_node
3018            .values()
3019            .fold(0usize, |total, recovered| {
3020                recovered.iter().fold(
3021                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3022                    |acc, class| {
3023                        acc.saturating_add(std::mem::size_of::<
3024                            RecoveredEmbeddedFunctionLikeExportClass,
3025                        >())
3026                        .saturating_add(class.name.len())
3027                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3028                    },
3029                )
3030            })
3031    }
3032
3033    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3034        self.by_error_node
3035            .get(&(node.start_byte(), node.end_byte()))
3036            .is_some_and(|recovered| {
3037                recovered.iter().any(|class| {
3038                    class.name == identifier
3039                        && class.range.start_byte == range.start_byte
3040                        && class.range.end_byte == range.end_byte
3041                })
3042            })
3043    }
3044}
3045
3046// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3047// `recovered_class_body_at` pops while deciding whether a recovered class shape
3048// claims one declaration range. The count is deterministic for a given source,
3049// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3050// pins it directly instead of timing the walk, the way #2358 pinned the
3051// `remove_code_unit` scan.
3052#[cfg(any(test, feature = "test-support"))]
3053thread_local! {
3054    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3055        const { std::cell::Cell::new(0) };
3056}
3057
3058/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3059/// the calling thread since the last
3060/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3061#[cfg(any(test, feature = "test-support"))]
3062#[doc(hidden)]
3063pub fn recovered_class_body_node_visits_for_test() -> usize {
3064    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3065}
3066
3067/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3068#[cfg(any(test, feature = "test-support"))]
3069#[doc(hidden)]
3070pub fn reset_recovered_class_body_node_visits_for_test() {
3071    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3072}
3073
3074#[cfg(any(test, feature = "test-support"))]
3075fn record_recovered_class_body_visit() {
3076    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3077}
3078
3079#[cfg(not(any(test, feature = "test-support")))]
3080fn record_recovered_class_body_visit() {}
3081
3082/// Whether a recovered class shape named `identifier` owns `range`, and if so
3083/// whether that shape has a body.
3084///
3085/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3086/// forward declaration, and `None` means no recovered shape claims the range,
3087/// so the caller reads the plain `class_specifier` family instead. This is the
3088/// single definition of "does a recovered class have a body": the resolver's
3089/// declaration-strength answer and the navigation occurrence role both read it,
3090/// so an export-macro class is a definition on both paths.
3091///
3092/// The walk follows only the nodes whose span covers `range.start_byte`, which
3093/// is every node that can answer. Each recovered shape reports a range that
3094/// starts at the node's own start byte (the function-like export pair, whose
3095/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3096/// class, which the caller gates on an equal start) or at a token inside the
3097/// node (the embedded export class, keyed on its `class` token, and the
3098/// exported class wrapper, gated on containment here), and every one of them is
3099/// accepted only on an exact match with `range`. Descending everywhere instead
3100/// made one declaration-strength question cost a full pass over the file, so
3101/// asking it once per reference was quadratic in file size: 80% of the 385 s
3102/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3103/// (#1496).
3104pub(crate) fn recovered_class_body_at(
3105    recovered_export_classes: &CppRecoveredExportClassIndex,
3106    root: Node<'_>,
3107    source: &str,
3108    identifier: &str,
3109    range: &Range,
3110) -> Option<bool> {
3111    let covers_range_start = |node: &Node<'_>| {
3112        node.start_byte() <= range.start_byte
3113            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3114    };
3115    let mut stack = vec![root];
3116    let mut saw_forward = false;
3117    while let Some(node) = stack.pop() {
3118        record_recovered_class_body_visit();
3119        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3120        // so an unequal start settles it before the recovery reads the node's
3121        // children at all.
3122        if (node.start_byte() == range.start_byte
3123            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3124            || recovered_export_classes.claims(node, identifier, range)
3125            || (node.start_byte() == range.start_byte
3126                && recovered_fragmented_class_has_body(node, source, identifier, range))
3127        {
3128            return Some(true);
3129        }
3130        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3131            return Some(true);
3132        }
3133        // Macro-decorated exported classes are recovered from a malformed
3134        // function_definition/declaration wrapper. Their indexed class range starts at
3135        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3136        // may also extend the indexed range beyond the wrapper through trailing class
3137        // fragments. Match the structured container that owns the range start by its
3138        // recovered name instead of requiring identical boundaries.
3139        if node.start_byte() <= range.start_byte
3140            && range.start_byte < node.end_byte()
3141            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3142        {
3143            if has_body {
3144                return Some(true);
3145            }
3146            saw_forward = true;
3147            continue;
3148        }
3149        let mut cursor = node.walk();
3150        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3151    }
3152    saw_forward.then_some(false)
3153}
3154
3155/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3156/// head of `identifier`'s member list at `range`.
3157///
3158/// An aggregate whose member list opens with a field-list macro invocation
3159/// never forms a specifier, so the ordinary strength read finds no specifier
3160/// node at the indexed range and answers `Unknown`. A complete definition
3161/// recovered this way then loses the defining-type choice to a forward
3162/// declaration of the same tag in another header, and every member the
3163/// definition declares becomes unreachable through it: libuv's
3164/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3165/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3166/// proves the body, so it answers for its own ranges.
3167fn recovered_collapsed_aggregate_has_body(
3168    node: Node<'_>,
3169    source: &str,
3170    identifier: &str,
3171    range: &Range,
3172) -> bool {
3173    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3174        head.key.start_byte() == range.start_byte
3175            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3176    };
3177    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3178        return true;
3179    }
3180    if !node.is_error() {
3181        return false;
3182    }
3183    let mut cursor = node.walk();
3184    let children = node.children(&mut cursor).collect::<Vec<_>>();
3185    children
3186        .iter()
3187        .enumerate()
3188        .filter(|(_, child)| {
3189            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3190        })
3191        .any(|(index, _)| {
3192            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3193        })
3194}
3195
3196/// Whether `node` is the base type displaced into the declarator field of an
3197/// export-macro class that tree-sitter represented as a declaration or
3198/// function definition.
3199///
3200/// Declaration extraction already recovers this exact malformed envelope as a
3201/// class and records the declarator as its base. Reference extraction must use
3202/// the same structural fact instead of treating the node as a function name.
3203pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3204    if !matches!(
3205        node.kind(),
3206        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3207    ) {
3208        return false;
3209    }
3210    if let Some(function) = node.parent().filter(|parent| {
3211        parent.kind() == "function_definition"
3212            && parent
3213                .child_by_field_name("declarator")
3214                .is_some_and(|declarator| same_node(declarator, node))
3215    }) {
3216        return recover_exported_class_function_definition(function, source)
3217            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3218    }
3219    let Some(initializer) = node.parent().filter(|parent| {
3220        parent.kind() == "init_declarator"
3221            && parent
3222                .child_by_field_name("declarator")
3223                .is_some_and(|declarator| same_node(declarator, node))
3224    }) else {
3225        return false;
3226    };
3227    initializer
3228        .parent()
3229        .filter(|parent| parent.kind() == "declaration")
3230        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3231        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3232}
3233
3234/// Recover the class item from a region reparse that still carries the
3235/// sentinel's synthetic function envelope.  An unknown class attribute can
3236/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3237/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3238/// then nested below that function, so direct class-child lookup is not enough.
3239struct CppSentinelReparsedClass<'tree> {
3240    declaration_node: Node<'tree>,
3241    name: String,
3242    body: Node<'tree>,
3243    raw_supertypes: Option<Vec<String>>,
3244}
3245
3246fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3247    let mut cursor = root.walk();
3248    root.named_children(&mut cursor)
3249        .find(|child| child.kind() != "comment")
3250        .filter(|child| child.kind() == "template_declaration")
3251}
3252
3253fn cpp_sentinel_reparsed_class<'tree>(
3254    root: Node<'tree>,
3255    template_node: Option<Node<'tree>>,
3256    source: &str,
3257    ancestry: &ParentIndex<'tree>,
3258) -> Option<CppSentinelReparsedClass<'tree>> {
3259    let container = template_node.unwrap_or(root);
3260    let mut cursor = container.walk();
3261    for child in container.named_children(&mut cursor) {
3262        if matches!(
3263            child.kind(),
3264            "class_specifier" | "struct_specifier" | "union_specifier"
3265        ) {
3266            let name = class_like_name(child, source, ancestry)?;
3267            let body = cpp_body_node(child)?;
3268            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3269                .then(|| extract_cpp_supertypes(child, source));
3270            return Some(CppSentinelReparsedClass {
3271                declaration_node: child,
3272                name,
3273                body,
3274                raw_supertypes,
3275            });
3276        }
3277        if child.kind() == "declaration"
3278            && let Some(class_node) = first_class_like_child(child)
3279        {
3280            let name = class_like_name(class_node, source, ancestry)?;
3281            let body = cpp_body_node(class_node)?;
3282            let raw_supertypes =
3283                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3284                    .then(|| extract_cpp_supertypes(class_node, source));
3285            return Some(CppSentinelReparsedClass {
3286                declaration_node: class_node,
3287                name,
3288                body,
3289                raw_supertypes,
3290            });
3291        }
3292        // Only when the nested class item carries its own body. A bodyless
3293        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3294        // a function definition -- is the export-macro shape recovered by the
3295        // next arm, and must fall through to it rather than abort the search.
3296        if child.kind() == "function_definition"
3297            && let Some(class_node) = first_class_like_child(child)
3298            && let Some(body) = cpp_body_node(class_node)
3299            && let Some(name) = class_like_name(class_node, source, ancestry)
3300        {
3301            let raw_supertypes =
3302                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3303                    .then(|| extract_cpp_supertypes(class_node, source));
3304            return Some(CppSentinelReparsedClass {
3305                declaration_node: class_node,
3306                name,
3307                body,
3308                raw_supertypes,
3309            });
3310        }
3311        if child.kind() == "function_definition"
3312            && let Some((_, name, raw_supertypes)) =
3313                recover_exported_class_function_definition(child, source)
3314        {
3315            let body = cpp_body_node(child)?;
3316            return Some(CppSentinelReparsedClass {
3317                declaration_node: child,
3318                name,
3319                body,
3320                raw_supertypes,
3321            });
3322        }
3323    }
3324    None
3325}
3326
3327fn recovered_postfix_export_macro_base(
3328    node: Node<'_>,
3329    type_node: Node<'_>,
3330    declarator: Node<'_>,
3331    source: &str,
3332) -> Option<String> {
3333    let mut cursor = node.walk();
3334    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3335        child.kind() == "ERROR"
3336            && child.start_byte() >= type_node.end_byte()
3337            && child.end_byte() <= declarator.start_byte()
3338            && postfix_export_macro_inheritance(*child, source)
3339    });
3340    malformed_clauses.next()?;
3341    if malformed_clauses.next().is_some() {
3342        return None;
3343    }
3344    recovered_malformed_base_name(declarator, source)
3345}
3346
3347fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3348    let mut macro_count = 0;
3349    let mut colon_count = 0;
3350    let mut access_count = 0;
3351    for index in 0..node.child_count() {
3352        let Some(child) = node.child(index) else {
3353            return false;
3354        };
3355        match child.kind() {
3356            "identifier" | "type_identifier" if child.is_named() => {
3357                let candidate = normalize_cpp_whitespace(node_text(child, source));
3358                if !cpp_export_macro_token(&candidate) {
3359                    return false;
3360                }
3361                macro_count += 1;
3362            }
3363            ":" if !child.is_named() => colon_count += 1,
3364            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3365            _ => return false,
3366        }
3367    }
3368    macro_count == 1 && colon_count == 1 && access_count == 1
3369}
3370
3371fn recovered_single_base_after_declarator(
3372    node: Node<'_>,
3373    declarator: Node<'_>,
3374    source: &str,
3375) -> Option<String> {
3376    let body_start = node
3377        .child_by_field_name("body")
3378        .map(|body| body.start_byte())
3379        .unwrap_or(node.end_byte());
3380    let mut cursor = node.walk();
3381    let mut bases = node
3382        .named_children(&mut cursor)
3383        .filter(|child| {
3384            child.kind() == "ERROR"
3385                && child.start_byte() >= declarator.end_byte()
3386                && child.end_byte() <= body_start
3387        })
3388        .filter_map(|error| displaced_exported_class_name(error, source));
3389    let base = bases.next()?;
3390    bases.next().is_none().then_some(base)
3391}
3392
3393fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3394    (0..node.child_count()).any(|index| {
3395        node.child(index)
3396            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3397    })
3398}
3399
3400pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3401    recover_exported_class_function_definition(node, source).is_some()
3402}
3403
3404fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3405    matches!(
3406        kind,
3407        "ERROR"
3408            | "preproc_if"
3409            | "preproc_ifdef"
3410            | "preproc_ifndef"
3411            | "preproc_else"
3412            | "preproc_elif"
3413    ) || (kind == "labeled_statement" && in_class_scope)
3414}
3415
3416pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3417    if node.kind() != "declaration" {
3418        return false;
3419    }
3420    let mut ancestor = node.parent();
3421    while let Some(container) = ancestor {
3422        match container.kind() {
3423            "compound_statement" => {
3424                return container.parent().is_some_and(|class_container| {
3425                    is_recovered_exported_class_container(class_container, source)
3426                });
3427            }
3428            // These containers preserve ScopeInfo in visit_node. declaration_list is
3429            // the body container selected for a linkage specification.
3430            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3431            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3432            _ => return false,
3433        }
3434        ancestor = container.parent();
3435    }
3436    false
3437}
3438
3439pub fn recovered_exported_class_has_body(
3440    node: Node<'_>,
3441    source: &str,
3442    expected_name: &str,
3443) -> Option<bool> {
3444    match node.kind() {
3445        "function_definition" => {
3446            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3447            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3448        }
3449        "declaration" | "field_declaration" => {
3450            let recovered = recover_exported_class_declaration(node, source)?;
3451            (recovered.name == expected_name).then(|| recovered.body.is_some())
3452        }
3453        _ => None,
3454    }
3455}
3456
3457fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3458    let body_start = node
3459        .child_by_field_name("body")
3460        .map(|body| body.start_byte())
3461        .unwrap_or(node.end_byte());
3462    let mut stack = Vec::new();
3463    for index in (0..node.named_child_count()).rev() {
3464        let Some(child) = node.named_child(index) else {
3465            continue;
3466        };
3467        if child.start_byte() >= body_start {
3468            continue;
3469        }
3470        stack.push(child);
3471    }
3472
3473    let mut best = None;
3474    while let Some(current) = stack.pop() {
3475        if matches!(current.kind(), "identifier" | "type_identifier") {
3476            let name = normalize_cpp_whitespace(node_text(current, source));
3477            if !name.is_empty()
3478                && !cpp_export_macro_token(&name)
3479                && !matches!(name.as_str(), "class" | "struct" | "union")
3480            {
3481                best = Some(name);
3482            }
3483            continue;
3484        }
3485
3486        for index in (0..current.named_child_count()).rev() {
3487            if let Some(child) = current.named_child(index)
3488                && child.start_byte() < body_start
3489            {
3490                stack.push(child);
3491            }
3492        }
3493    }
3494    best
3495}
3496
3497fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3498    if node.kind() == "declaration"
3499        && node
3500            .child_by_field_name("type")
3501            .or_else(|| first_class_like_child(node))
3502            .is_some_and(|type_node| {
3503                matches!(
3504                    type_node.kind(),
3505                    "class_specifier" | "struct_specifier" | "union_specifier"
3506                )
3507            })
3508        && let Some(name) = node
3509            .child_by_field_name("declarator")
3510            .and_then(|declarator| declarator_name_from_node(declarator, source))
3511        && !cpp_export_macro_token(&name)
3512    {
3513        return Some(name);
3514    }
3515
3516    if node.kind() == "function_definition"
3517        && node.child_by_field_name("type").is_some_and(|type_node| {
3518            matches!(
3519                type_node.kind(),
3520                "class_specifier" | "struct_specifier" | "union_specifier"
3521            )
3522        })
3523        && let Some(name) = node
3524            .child_by_field_name("declarator")
3525            .and_then(|declarator| direct_identifier_name(declarator, source))
3526        && !cpp_export_macro_token(&name)
3527    {
3528        return Some(name);
3529    }
3530
3531    let class_node = if matches!(
3532        node.kind(),
3533        "class_specifier" | "struct_specifier" | "union_specifier"
3534    ) {
3535        node
3536    } else {
3537        first_class_like_child(node)?
3538    };
3539    class_like_name_from_children(class_node, source)
3540}
3541
3542fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3543    if !matches!(
3544        node.kind(),
3545        "identifier" | "field_identifier" | "type_identifier"
3546    ) {
3547        return None;
3548    }
3549    let name = normalize_cpp_whitespace(node_text(node, source));
3550    (!name.is_empty()).then_some(name)
3551}
3552
3553fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3554    match node.kind() {
3555        "identifier" | "field_identifier" | "type_identifier" => {
3556            let name = normalize_cpp_whitespace(node_text(node, source));
3557            (!name.is_empty()).then_some(name)
3558        }
3559        _ => {
3560            let mut cursor = node.walk();
3561            node.named_children(&mut cursor)
3562                .find_map(|child| declarator_name_from_node(child, source))
3563        }
3564    }
3565}
3566
3567fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3568    let mut cursor = node.walk();
3569    node.named_children(&mut cursor).find(|child| {
3570        matches!(
3571            child.kind(),
3572            "class_specifier" | "struct_specifier" | "union_specifier"
3573        )
3574    })
3575}
3576
3577/// Push a container's children as a `Siblings` cursor rather than snapshotting
3578/// them all with one shared scope: children are visited one at a time so a
3579/// `using namespace X;` sibling can affect the scope threaded to the siblings
3580/// that textually follow it (issue #1093).
3581fn push_cpp_container_work<'tree>(
3582    node: Node<'tree>,
3583    scope: ScopeInfo,
3584    stack: &mut Vec<CppWork<'tree>>,
3585) {
3586    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3587}
3588
3589/// Materialize one selected named-child range with a tree-sitter cursor. The
3590/// cursor advances linearly across the parent's concrete children; repeatedly
3591/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3592fn push_cpp_sibling_range<'tree>(
3593    parent: Node<'tree>,
3594    start_index: usize,
3595    end_index: usize,
3596    scope: ScopeInfo,
3597    stack: &mut Vec<CppWork<'tree>>,
3598) {
3599    let mut cursor = parent.walk();
3600    let children = parent
3601        .named_children(&mut cursor)
3602        .skip(start_index)
3603        .take(end_index.saturating_sub(start_index))
3604        .collect::<Vec<_>>()
3605        .into_iter();
3606    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3607}
3608
3609/// Advance a `Siblings` cursor by one child: dispatch the current child under
3610/// the scope accumulated from its *earlier* siblings, then push a
3611/// continuation for the remaining siblings carrying the scope updated for
3612/// *this* child (only `using namespace X;` directives change it). Pushing the
3613/// continuation before the current child's own node work means the current
3614/// child's subtree fully drains (LIFO) before the next sibling is visited,
3615/// preserving left-to-right order.
3616fn advance_cpp_siblings<'tree>(
3617    mut siblings: CppSiblingsWork<'tree>,
3618    source: &str,
3619    stack: &mut Vec<CppWork<'tree>>,
3620) {
3621    let Some(child) = siblings.children.next() else {
3622        return;
3623    };
3624    let current_scope = siblings.scope.clone();
3625    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3626        siblings.scope.visible_using_namespaces.push(namespace);
3627    }
3628    if !siblings.children.as_slice().is_empty() {
3629        stack.push(CppWork::Siblings(siblings));
3630    }
3631    stack.push(CppWork::Node(CppNodeWork {
3632        node: child,
3633        scope: current_scope,
3634    }));
3635}
3636
3637/// The namespace target of a `using namespace X;` directive, or `None` for
3638/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3639/// kind. Distinguished structurally by the presence of the grammar's literal
3640/// `namespace` keyword token among the node's children -- not by inspecting
3641/// source text -- so it never misreads a member-importing using-declaration
3642/// as a namespace directive.
3643fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3644    if node.kind() != "using_declaration" {
3645        return None;
3646    }
3647    let mut cursor = node.walk();
3648    let is_namespace_directive = node
3649        .children(&mut cursor)
3650        .any(|child| child.kind() == "namespace");
3651    if !is_namespace_directive {
3652        return None;
3653    }
3654    let target = node.named_child(0)?;
3655    // A leading `::` is the explicit-global marker, not part of the namespace
3656    // path (`using namespace ::std::chrono;`). Drop that AST token before
3657    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3658    // keeps: storing the marker verbatim desynced the legacy package string from
3659    // the FqName bridge, which splits on `::` and drops the empty leading
3660    // component, tripping the package/short boundary assert when a bare-owner
3661    // out-of-line definition borrowed the directive's namespace (#1093 path).
3662    let start = target
3663        .child(0)
3664        .filter(|child| !child.is_named() && child.kind() == "::")
3665        .map_or(target.start_byte(), |marker| marker.end_byte());
3666    let text = normalize_cpp_whitespace(
3667        source
3668            .get(start..target.end_byte())
3669            .expect("using-directive target covers one source range"),
3670    );
3671    (!text.is_empty()).then_some(text)
3672}
3673
3674/// Every `using namespace X;` directive target in a file, in source order, for
3675/// resolution-time consumers that need the file's using-directives without the
3676/// per-position scope threading extraction does. Parses `source` fresh and
3677/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3678/// keys on the grammar's `namespace` keyword token, not source text), so it
3679/// never misreads a member-importing `using X::Y;` as a namespace directive.
3680///
3681/// This is a whole-file over-approximation of what is in scope at any one point
3682/// (a directive nested inside a `namespace {}` block or a function body is still
3683/// reported), which is exactly what the #1134 identity reconciler wants: extra
3684/// candidate namespaces that no visible class confirms are harmless, and two
3685/// that both confirm are treated as a genuine ambiguity by the reconciler.
3686pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3687    let mut parser = Parser::new();
3688    if parser
3689        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3690        .is_err()
3691    {
3692        return Vec::new();
3693    }
3694    let Some(tree) = parser.parse(source, None) else {
3695        return Vec::new();
3696    };
3697    let mut namespaces = Vec::new();
3698    let mut seen = std::collections::HashSet::new();
3699    let mut stack = vec![tree.root_node()];
3700    while let Some(node) = stack.pop() {
3701        if let Some(namespace) = cpp_using_namespace_target(node, source)
3702            && seen.insert(namespace.clone())
3703        {
3704            namespaces.push(namespace);
3705        }
3706        let mut cursor = node.walk();
3707        stack.extend(node.named_children(&mut cursor));
3708    }
3709    namespaces
3710}
3711
3712pub struct CppVisitor<'a> {
3713    pub file: &'a ProjectFile,
3714    pub source: &'a str,
3715    pub parsed: &'a mut ParsedFile,
3716    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3717    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3718    ///
3719    /// C has no nested tag scope: a struct/union/enum tag declared inside
3720    /// another aggregate's member list has the scope of the outer declaration
3721    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3722    /// therefore declares a file-scope `inner` that a later file-scope
3723    /// `struct inner *p;` legitimately references, where C++ would make the
3724    /// same shape a nested class `outer::inner`. Headers carry no compilation
3725    /// language of their own and keep the conservative C++ interpretation.
3726    pub c_tag_semantics: bool,
3727    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3728    /// Byte regions whose contents were re-owned by a fragmented export-class
3729    /// recovery (#938): the scattered members between the fragmented
3730    /// declaration and its displaced closing brace are indexed as members of
3731    /// the recovered class by the region reparse, so the ordinary sibling walk
3732    /// must not ALSO index them as top-level declarations (that double-indexing
3733    /// made a scattered nested class ambiguous between `Inner` and
3734    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3735    /// linear scan at visit time is fine.
3736    pub consumed_fragment_regions: Vec<(usize, usize)>,
3737    /// Lexical namespace corrections and brace boundaries for declarations
3738    /// displaced by parse recovery (issues #1537 and #3087). The parsed
3739    /// ancestors can stop short or extend past their true closes; see
3740    /// [`CppVisitor::recovered_namespace_scope`].
3741    pub orphaned_namespaces: OrphanedNamespaceScopeIndex,
3742    /// Owned reparses waiting for the outer work loop. Partitioning another
3743    /// swallowed class in a tail must not grow the Rust call stack.
3744    pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3745    /// The namespace forward declarations already folded out of each tree this
3746    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3747    /// Empty until the first question, which the overwhelming majority of files
3748    /// never ask.
3749    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3750    /// Which owners already have field declarations in the parse product, as
3751    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3752    /// the first enum asks, which most files never do (#2786).
3753    pub field_owners: Option<CppFieldOwnerIndex>,
3754    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3755    /// happen to the declaration set, innermost last. Empty outside a recovery
3756    /// reparse, which is almost always (#2787).
3757    pub recovery_captures: Vec<CppRecoveryCapture>,
3758    /// Object-like field-list macros defined earlier in this source. Their
3759    /// replacements are parsed structurally and materialized under each
3760    /// invoking aggregate; no source-text expansion is used.
3761    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3762    /// Names whose active replacement is not a unique structured field list.
3763    /// A later `#undef` resets the ambiguity; another `#define` does not.
3764    pub ambiguous_object_macro_fields: HashSet<String>,
3765}
3766
3767/// One source-order event from an object-like field-list macro environment.
3768///
3769/// The event stream lets an include-closure consumer preserve preprocessor
3770/// invalidation without guessing which parsed header happened to be visited
3771/// first. A conservative consumer may permanently block a name after an
3772/// `undef` when conditional include order is not provable.
3773#[derive(Clone, Debug, PartialEq, Eq)]
3774pub enum ObjectMacroFieldEvent {
3775    Define {
3776        name: String,
3777        replacement: ObjectMacroReplacement,
3778        conditional: bool,
3779    },
3780    Undef {
3781        name: String,
3782        conditional: bool,
3783    },
3784}
3785
3786/// Collect object-like field-list macros from a parsed source in source order.
3787/// Macros with conflicting active replacements are omitted so an include
3788/// closure cannot silently choose one guarded definition over another.
3789pub fn collect_cpp_object_macro_fields<'tree>(
3790    root: Node<'tree>,
3791    source: &str,
3792) -> HashMap<String, ObjectMacroReplacement> {
3793    let mut fields = HashMap::default();
3794    let mut ambiguous = HashSet::default();
3795    for event in collect_cpp_object_macro_field_events(root, source) {
3796        match event {
3797            ObjectMacroFieldEvent::Define {
3798                name,
3799                replacement: value,
3800                conditional,
3801            } => {
3802                if value.is_empty() {
3803                    fields.remove(&name);
3804                    if conditional {
3805                        ambiguous.insert(name);
3806                    } else {
3807                        ambiguous.remove(&name);
3808                    }
3809                } else if ambiguous.contains(&name) {
3810                    // Keep the name blocked until an explicit undef resets it.
3811                } else if let Some(previous) = fields.get(&name) {
3812                    if previous != &value {
3813                        fields.remove(&name);
3814                        ambiguous.insert(name);
3815                    }
3816                } else {
3817                    fields.insert(name, value);
3818                }
3819            }
3820            ObjectMacroFieldEvent::Undef { name, conditional } => {
3821                fields.remove(&name);
3822                if conditional {
3823                    ambiguous.insert(name);
3824                } else {
3825                    ambiguous.remove(&name);
3826                }
3827            }
3828        }
3829    }
3830    fields
3831}
3832
3833/// Collect object-like field-list macro events in source order. Conditional
3834/// branches remain visible in the event stream: callers that cannot prove
3835/// branch selection can invalidate names conservatively instead of inventing
3836/// a replacement from one branch.
3837pub fn collect_cpp_object_macro_field_events<'tree>(
3838    root: Node<'tree>,
3839    source: &str,
3840) -> Vec<ObjectMacroFieldEvent> {
3841    let mut events = Vec::new();
3842    let mut stack = vec![root];
3843    while let Some(node) = stack.pop() {
3844        if node.kind() == "preproc_def"
3845            && let Some(name) = extract_macro_name(node, source)
3846        {
3847            let replacement = object_macro_replacement_of(node, source);
3848            events.push(ObjectMacroFieldEvent::Define {
3849                name,
3850                replacement,
3851                conditional: inside_preprocessor_conditional(node),
3852            });
3853        } else if is_cpp_undef_directive(node, source)
3854            && let Some(argument) = node.child_by_field_name("argument")
3855        {
3856            events.push(ObjectMacroFieldEvent::Undef {
3857                name: node_text(argument, source).trim().to_string(),
3858                conditional: inside_preprocessor_conditional(node),
3859            });
3860        }
3861        let mut cursor = node.walk();
3862        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3863        stack.extend(children.into_iter().rev());
3864    }
3865    events
3866}
3867
3868fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
3869    let mut current = node.parent();
3870    while let Some(parent) = current {
3871        if matches!(
3872            parent.kind(),
3873            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
3874        ) {
3875            return true;
3876        }
3877        current = parent.parent();
3878    }
3879    false
3880}
3881
3882fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
3883    node.kind() == "preproc_call"
3884        && node
3885            .child_by_field_name("directive")
3886            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
3887}
3888
3889impl<'a> CppVisitor<'a> {
3890    /// Records `code_unit` with the answers the walk carries forward, then adds
3891    /// it to the parse product.
3892    ///
3893    /// Every declaration this walk publishes goes through this family, so the
3894    /// carried-forward answers see each one exactly once: the field ownership
3895    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
3896    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
3897    fn add_declaration(
3898        &mut self,
3899        code_unit: CodeUnit,
3900        node: Node<'_>,
3901        parent: Option<CodeUnit>,
3902        top_level: Option<CodeUnit>,
3903    ) {
3904        self.note_declaration(&code_unit);
3905        let source = self.source;
3906        self.parsed
3907            .add_code_unit(code_unit, node, source, parent, top_level);
3908    }
3909
3910    /// Range-based form of [`Self::add_declaration`].
3911    fn add_declaration_with_range(
3912        &mut self,
3913        code_unit: CodeUnit,
3914        range: Range,
3915        parent: Option<CodeUnit>,
3916        top_level: Option<CodeUnit>,
3917    ) {
3918        self.note_declaration(&code_unit);
3919        self.parsed
3920            .add_code_unit_with_range(code_unit, range, parent, top_level);
3921    }
3922
3923    /// Deferred-replacement form of [`Self::add_declaration`].
3924    fn replace_declaration_deferred(
3925        &mut self,
3926        code_unit: CodeUnit,
3927        node: Node<'_>,
3928        parent: Option<CodeUnit>,
3929        top_level: Option<CodeUnit>,
3930    ) {
3931        self.note_replaced_declaration(&code_unit);
3932        let source = self.source;
3933        self.parsed
3934            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
3935    }
3936
3937    /// Range-based form of [`Self::replace_declaration_deferred`].
3938    fn replace_declaration_with_range_deferred(
3939        &mut self,
3940        code_unit: CodeUnit,
3941        range: Range,
3942        parent: Option<CodeUnit>,
3943        top_level: Option<CodeUnit>,
3944    ) {
3945        self.note_replaced_declaration(&code_unit);
3946        self.parsed
3947            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
3948    }
3949
3950    /// Notes one declaration about to enter the parse product.
3951    ///
3952    /// A declaration the product already holds is not a creation, so an open
3953    /// recovery capture ignores it -- which is the membership test the set
3954    /// difference it replaces performed. A creation inside a nested recovery
3955    /// belongs to the recoveries around it too, so every open capture takes it.
3956    fn note_declaration(&mut self, code_unit: &CodeUnit) {
3957        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
3958            for capture in &mut self.recovery_captures {
3959                if capture.removed_pre_existing.contains(code_unit) {
3960                    continue;
3961                }
3962                if capture.created_units.insert(code_unit.clone()) {
3963                    capture.created.push(code_unit.clone());
3964                }
3965            }
3966        }
3967        if let Some(field_owners) = self.field_owners.as_mut() {
3968            field_owners.record(code_unit, self.file);
3969        }
3970    }
3971
3972    /// Notes one declaration about to replace an existing one.
3973    ///
3974    /// A deferred replacement of a declaration that already owns children
3975    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
3976    /// a removal is the one thing the field index cannot absorb by addition.
3977    /// Drop it; the next question rebuilds it from the declarations that
3978    /// survive. A replacement of a unit with no children, and a "replacement"
3979    /// of a unit that is not there at all, remove nothing.
3980    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
3981        let removes_children = self.parsed.contains_declaration(code_unit)
3982            && self
3983                .parsed
3984                .children
3985                .get(code_unit)
3986                .is_some_and(|children| !children.is_empty());
3987        if removes_children {
3988            if !self.recovery_captures.is_empty() {
3989                let removed = self.declarations_a_replacement_removes(code_unit);
3990                for capture in &mut self.recovery_captures {
3991                    for unit in &removed {
3992                        // A declaration this capture watched being created is
3993                        // its own; one it did not is a declaration that was
3994                        // already there when the capture opened, so creating it
3995                        // again is a restoration and not a mint.
3996                        if !capture.created_units.contains(unit) {
3997                            capture.removed_pre_existing.insert(unit.clone());
3998                        }
3999                    }
4000                }
4001            }
4002            self.field_owners = None;
4003        }
4004        self.note_declaration(code_unit);
4005    }
4006
4007    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
4008    /// when `code_unit` is replaced: its children, transitively.
4009    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4010        let mut removed = Vec::new();
4011        let mut seen = HashSet::default();
4012        let mut pending: Vec<CodeUnit> = self
4013            .parsed
4014            .children
4015            .get(code_unit)
4016            .cloned()
4017            .unwrap_or_default();
4018        while let Some(unit) = pending.pop() {
4019            if !seen.insert(unit.clone()) {
4020                continue;
4021            }
4022            if let Some(children) = self.parsed.children.get(&unit) {
4023                pending.extend(children.iter().cloned());
4024            }
4025            removed.push(unit);
4026        }
4027        removed
4028    }
4029
4030    fn visit_function_like_export_class_pair<'tree>(
4031        &mut self,
4032        node: Node<'tree>,
4033        scope: &ScopeInfo,
4034        stack: &mut Vec<CppWork<'tree>>,
4035        ancestry: &ParentIndex<'tree>,
4036    ) -> bool {
4037        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4038            return false;
4039        };
4040        let member_outcome = self
4041            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4042        // A malformed class body can escape into several following siblings
4043        // before the next export-macro class head appears. Inspect siblings in
4044        // order and stop at the first envelope that contains such a head. One
4045        // envelope can contain several following classes, all recovered in a
4046        // single bounded traversal.
4047        let mut displaced = node.next_named_sibling();
4048        while let Some(candidate) = displaced {
4049            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4050                break;
4051            }
4052            displaced = candidate.next_named_sibling();
4053        }
4054        let class_unit = self.visit_named_class_like_shape(
4055            node,
4056            recovered.name,
4057            // The adjacent initializer_list proves the class body envelope,
4058            // but its children are expression-shaped rather than declaration-
4059            // preserving. Index the class identity here; callable definitions
4060            // remain available from their ordinary out-of-line declarations.
4061            None,
4062            true,
4063            Some(recovered.range),
4064            recovered.raw_supertypes,
4065            scope,
4066            stack,
4067            ancestry,
4068        );
4069        self.parsed
4070            .record_materialization(MaterializationRecord::RecoveredDeclaration {
4071                recovery: recovered.range,
4072                unit: class_unit.clone(),
4073            });
4074        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4075            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4076                tree.root_node(),
4077                class_unit.identifier(),
4078                self.source,
4079            )
4080        {
4081            self.visit_recovered_fragment_constructor(
4082                range,
4083                body,
4084                node,
4085                &class_unit,
4086                scope,
4087                ancestry,
4088            );
4089        }
4090        if let Some(outcome) = member_outcome {
4091            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4092        }
4093        self.consumed_fragment_regions
4094            .push((node.start_byte(), recovered.range.end_byte));
4095        true
4096    }
4097
4098    fn visit_embedded_function_like_export_classes<'tree>(
4099        &mut self,
4100        node: Node<'tree>,
4101        scope: &ScopeInfo,
4102        stack: &mut Vec<CppWork<'tree>>,
4103        ancestry: &ParentIndex<'tree>,
4104    ) -> bool {
4105        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4106        let found = !recovered_classes.is_empty();
4107        for recovered in recovered_classes {
4108            let member_outcome = self.reparse_fragmented_export_class_members(
4109                &recovered.fragmented_body,
4110                &recovered.name,
4111            );
4112            let class_unit = self.visit_named_class_like_shape(
4113                node,
4114                recovered.name,
4115                None,
4116                true,
4117                Some(recovered.range),
4118                Some(recovered.raw_supertypes),
4119                scope,
4120                stack,
4121                ancestry,
4122            );
4123            self.parsed
4124                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4125                    recovery: recovered.range,
4126                    unit: class_unit.clone(),
4127                });
4128            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4129                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4130                    tree.root_node(),
4131                    class_unit.identifier(),
4132                    self.source,
4133                )
4134            {
4135                self.visit_recovered_fragment_constructor(
4136                    range,
4137                    body,
4138                    node,
4139                    &class_unit,
4140                    scope,
4141                    ancestry,
4142                );
4143            }
4144            if let Some(outcome) = member_outcome {
4145                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4146            }
4147        }
4148        found
4149    }
4150
4151    /// Walk `node`'s container, answering every ancestor question from
4152    /// `ancestry`.
4153    ///
4154    /// `ancestry` must index the tree `node` belongs to. The caller owns it
4155    /// because one tree can be walked more than once -- a header's C and C++
4156    /// readings are the same tree under different tag semantics -- and the
4157    /// parent relation is a property of the tree, not of the reading.
4158    #[allow(clippy::too_many_arguments)]
4159    pub fn visit_container<'tree>(
4160        &mut self,
4161        node: Node<'tree>,
4162        ancestry: &ParentIndex<'tree>,
4163        package_name: &str,
4164        module: Option<CodeUnit>,
4165        class_unit: Option<CodeUnit>,
4166        template_signature: Option<String>,
4167        visible_using_namespaces: Vec<String>,
4168    ) {
4169        let scope = ScopeInfo {
4170            package_name: package_name.to_string(),
4171            module,
4172            class_unit,
4173            template_signature,
4174            template_metadata: None,
4175            declarations_are_fields: false,
4176            recovered_specialization_member_scope: false,
4177            visible_using_namespaces,
4178        };
4179        // The work loop dispatches a container's children, never the container
4180        // itself, so an `ERROR` container that is the seed does not reach the
4181        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
4182        // breaks the parse leaves the whole translation unit as that seed
4183        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
4184        // macro-error-class recovery reads.
4185        if node.is_error() {
4186            self.visit_object_macro_error_classes(node, &scope);
4187        }
4188        self.run_container_work(node, scope, ancestry);
4189        while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4190            let root = tree.root_node();
4191            let container = root
4192                .descendant_for_byte_range(range.start, range.end)
4193                .expect("the queued container belongs to this tree");
4194            assert_eq!(container.byte_range(), range);
4195            self.run_container_work(container, scope, &ParentIndex::new(root));
4196        }
4197    }
4198
4199    /// Whether a work node lies entirely inside a byte region consumed by a
4200    /// fragmented export-class recovery (#938); such nodes were already indexed
4201    /// as members of the recovered class by the region reparse.
4202    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4203        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4204    }
4205
4206    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
4207    /// candidate a recovery is about to reparse but has not yet turned into a
4208    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
4209    /// prior structured recovery.
4210    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4211        self.consumed_fragment_regions
4212            .iter()
4213            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4214    }
4215
4216    /// Drive the container work loop from an explicit seed scope to completion. The
4217    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
4218    /// stays alive for the whole traversal.
4219    ///
4220    /// Every ancestor question this walk asks is answered from `ancestry`, which
4221    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
4222    /// node's position in the tree, which made a generated header with thousands
4223    /// of top-level declarations quadratic (#2361). The index is the caller's
4224    /// because it outlives any one walk: the file's tree is walked twice when a
4225    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
4226    /// builds its own index for its own tree.
4227    fn run_container_work<'tree>(
4228        &mut self,
4229        node: Node<'tree>,
4230        scope: ScopeInfo,
4231        ancestry: &ParentIndex<'tree>,
4232    ) {
4233        self.drain_cpp_work(
4234            vec![CppWork::Container(CppContainer { node, scope })],
4235            ancestry,
4236        );
4237    }
4238
4239    /// The work loop itself, from whatever seed the caller built.
4240    ///
4241    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
4242    /// that must walk only part of a reparsed tree seeds it with the sibling
4243    /// range it may walk instead, which keeps the `using namespace X;` scope
4244    /// accumulation `advance_cpp_siblings` performs.
4245    fn drain_cpp_work<'tree>(
4246        &mut self,
4247        mut stack: Vec<CppWork<'tree>>,
4248        ancestry: &ParentIndex<'tree>,
4249    ) {
4250        while let Some(work) = stack.pop() {
4251            match work {
4252                CppWork::Container(container) => {
4253                    push_cpp_container_work(container.node, container.scope, &mut stack);
4254                }
4255                CppWork::Siblings(siblings) => {
4256                    advance_cpp_siblings(siblings, self.source, &mut stack);
4257                }
4258                CppWork::Node(work) => {
4259                    if self.node_is_inside_consumed_fragment(work.node) {
4260                        continue;
4261                    }
4262                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4263                }
4264            }
4265        }
4266    }
4267
4268    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
4269    /// it only when the entire region is member-shaped. This validation must happen
4270    /// before registering the recovered class because a rejected speculative range
4271    /// must not leak into the ordinary recovery path.
4272    fn reparse_fragmented_export_class_members(
4273        &self,
4274        fragmented: &FragmentedExportBody,
4275        class_name: &str,
4276    ) -> Option<FragmentedExportMembers> {
4277        if fragmented.reparse_start >= fragmented.reparse_end {
4278            return None;
4279        }
4280        let tree = cpp_reparse_fragmented_class_body(
4281            self.source,
4282            fragmented.reparse_start,
4283            fragmented.reparse_end,
4284        )?;
4285        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4286            return Some(FragmentedExportMembers::Complete(tree));
4287        }
4288        let has_conditional_constructor = {
4289            let root = tree.root_node();
4290            let mut cursor = root.walk();
4291            root.named_children(&mut cursor).any(|child| {
4292                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4293            })
4294        };
4295        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4296    }
4297
4298    /// Index an already validated fragmented body as members of `class_unit`. The
4299    /// region reparse keeps each member's exact original byte and line positions.
4300    fn visit_fragmented_export_class_members(
4301        &mut self,
4302        outcome: FragmentedExportMembers,
4303        class_unit: CodeUnit,
4304        scope: &ScopeInfo,
4305    ) -> bool {
4306        let (tree, complete) = match outcome {
4307            FragmentedExportMembers::Complete(tree) => (tree, true),
4308            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4309        };
4310        let root = tree.root_node();
4311        let class_name = class_unit.identifier().to_string();
4312        let member_scope = ScopeInfo {
4313            // A recovered export-macro class may borrow its namespace from an
4314            // earlier forward declaration even when the malformed node itself
4315            // sits at file scope. Use the recovered class identity as the
4316            // authoritative package for reparsed members as well.
4317            package_name: class_unit.package_name().to_string(),
4318            module: scope.module.clone(),
4319            class_unit: Some(class_unit),
4320            template_signature: scope.template_signature.clone(),
4321            template_metadata: None,
4322            declarations_are_fields: true,
4323            recovered_specialization_member_scope: false,
4324            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4325        };
4326        if !complete {
4327            // A conditional beginning immediately after an access label can
4328            // fragment one constructor declaration while leaving the rest of
4329            // the class body as unsafe statement soup. Recover only that
4330            // structurally proven constructor and leave the outer-tree
4331            // siblings unconsumed for their ordinary walk.
4332            let mut cursor = root.walk();
4333            let constructors = root
4334                .named_children(&mut cursor)
4335                .filter_map(|child| {
4336                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4337                })
4338                .collect::<Vec<_>>();
4339            // The reparsed region is its own tree, so this drain walks it with
4340            // its own parent index.
4341            let reparsed_ancestry = ParentIndex::new(root);
4342            for constructor in constructors {
4343                let mut stack = Vec::new();
4344                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4345                while let Some(work) = stack.pop() {
4346                    match work {
4347                        CppWork::Container(container) => {
4348                            push_cpp_container_work(container.node, container.scope, &mut stack);
4349                        }
4350                        CppWork::Siblings(siblings) => {
4351                            advance_cpp_siblings(siblings, self.source, &mut stack);
4352                        }
4353                        CppWork::Node(work) => {
4354                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4355                        }
4356                    }
4357                }
4358            }
4359            return false;
4360        }
4361        // The reparsed region is its own tree, so this walk indexes it itself.
4362        self.run_container_work(root, member_scope, &ParentIndex::new(root));
4363        true
4364    }
4365
4366    fn visit_recovered_fragment_constructor<'tree>(
4367        &mut self,
4368        range: std::ops::Range<usize>,
4369        constructor_body: Node<'tree>,
4370        class_declaration: Node<'tree>,
4371        class_unit: &CodeUnit,
4372        scope: &ScopeInfo,
4373        ancestry: &ParentIndex<'tree>,
4374    ) {
4375        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4376            return;
4377        };
4378        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4379            tree.root_node(),
4380            range.start,
4381            class_unit.identifier(),
4382            self.source,
4383        ) else {
4384            return;
4385        };
4386        let member_scope = ScopeInfo {
4387            package_name: class_unit.package_name().to_string(),
4388            module: scope.module.clone(),
4389            class_unit: Some(class_unit.clone()),
4390            template_signature: scope.template_signature.clone(),
4391            template_metadata: None,
4392            declarations_are_fields: true,
4393            recovered_specialization_member_scope: false,
4394            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4395        };
4396        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4397        else {
4398            return;
4399        };
4400        debug_assert_eq!(function.name, class_unit.identifier());
4401        let code_unit = function.code_unit(self.file.clone());
4402        self.add_declaration_with_range(
4403            code_unit.clone(),
4404            Range {
4405                start_byte: function_declarator.start_byte(),
4406                end_byte: constructor_body.end_byte(),
4407                start_line: function_declarator.start_position().row + 1,
4408                end_line: constructor_body.end_position().row + 1,
4409            },
4410            None,
4411            None,
4412        );
4413        self.parsed.add_signature_with_metadata(
4414            code_unit.clone(),
4415            cpp_signature_metadata(
4416                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4417                function_declarator,
4418                self.source,
4419                ancestry,
4420            )
4421            .with_declaration_only(false)
4422            .with_callable_linkage(cpp_callable_linkage(
4423                class_declaration,
4424                self.source,
4425                ancestry,
4426            )),
4427        );
4428        self.parsed.add_child(class_unit.clone(), code_unit);
4429    }
4430
4431    fn visit_recovered_fragment_prefix_members<'tree>(
4432        &mut self,
4433        root: Node<'tree>,
4434        constructor_start: usize,
4435        class_unit: &CodeUnit,
4436        scope: &ScopeInfo,
4437        ancestry: &ParentIndex<'tree>,
4438    ) {
4439        let member_scope = ScopeInfo {
4440            package_name: class_unit.package_name().to_string(),
4441            module: scope.module.clone(),
4442            class_unit: Some(class_unit.clone()),
4443            template_signature: scope.template_signature.clone(),
4444            template_metadata: None,
4445            declarations_are_fields: true,
4446            recovered_specialization_member_scope: false,
4447            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4448        };
4449        let mut stack = vec![root];
4450        while let Some(current) = stack.pop() {
4451            if current.kind() == "comment" || current.start_byte() >= constructor_start {
4452                continue;
4453            }
4454            if current.end_byte() <= constructor_start
4455                && current.kind() != "translation_unit"
4456                && current.kind() != "labeled_statement"
4457                && current.kind() != "ERROR"
4458            {
4459                let mut work_stack = Vec::new();
4460                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4461                while let Some(work) = work_stack.pop() {
4462                    match work {
4463                        CppWork::Container(container) => {
4464                            push_cpp_container_work(
4465                                container.node,
4466                                container.scope,
4467                                &mut work_stack,
4468                            );
4469                        }
4470                        CppWork::Siblings(siblings) => {
4471                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
4472                        }
4473                        CppWork::Node(work) => {
4474                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4475                        }
4476                    }
4477                }
4478                continue;
4479            }
4480            if matches!(
4481                current.kind(),
4482                "translation_unit" | "labeled_statement" | "ERROR"
4483            ) {
4484                let mut cursor = current.walk();
4485                stack.extend(current.named_children(&mut cursor));
4486            }
4487        }
4488    }
4489
4490    fn visit_node<'tree>(
4491        &mut self,
4492        node: Node<'tree>,
4493        scope: &ScopeInfo,
4494        stack: &mut Vec<CppWork<'tree>>,
4495        ancestry: &ParentIndex<'tree>,
4496    ) {
4497        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4498            self.visit_node(node, &recovered_scope, stack, ancestry);
4499            return;
4500        }
4501        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4502            self.visit_node(node, &recovered_scope, stack, ancestry);
4503            return;
4504        }
4505        // Fragmented-class recovery below may consume a malformed function
4506        // envelope before the ordinary kind dispatch runs. Recover any
4507        // export-macro class embedded in that envelope first; the strict class
4508        // head/base/body predicate is independent of which later recovery owns
4509        // the surrounding parser fragment.
4510        if node.kind() == "function_definition" && node.has_error() {
4511            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4512        }
4513        if let Some(FragmentedClassRecovery {
4514            declaration_node: class_node,
4515            name,
4516            raw_supertypes,
4517            body: fragmented,
4518        }) = fragmented_class_body(node, self.source)
4519        {
4520            let displaced_namespace_items =
4521                displaced_fragment_namespace_geometry(node, self.source)
4522                    .map(|boundary| boundary.namespace_items)
4523                    .unwrap_or_default();
4524            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4525            let mut class_stack = Vec::new();
4526            // When the full body cannot be safely reparsed, the original class
4527            // node still proves ownership for its parser-visible prefix.
4528            let parser_visible_body =
4529                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4530                    .then(|| cpp_body_node(class_node))
4531                    .flatten();
4532            let class_unit = self.visit_named_class_like_shape(
4533                class_node,
4534                name,
4535                parser_visible_body,
4536                true,
4537                Some(fragmented.class_range),
4538                Some(raw_supertypes),
4539                scope,
4540                &mut class_stack,
4541                ancestry,
4542            );
4543            let member_scope = ScopeInfo {
4544                package_name: class_unit.package_name().to_string(),
4545                module: scope.module.clone(),
4546                class_unit: Some(class_unit.clone()),
4547                template_signature: scope.template_signature.clone(),
4548                template_metadata: None,
4549                declarations_are_fields: true,
4550                recovered_specialization_member_scope: false,
4551                visible_using_namespaces: scope.visible_using_namespaces.clone(),
4552            };
4553            let complete = outcome.is_some_and(|outcome| {
4554                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4555            });
4556            if complete {
4557                self.consumed_fragment_regions
4558                    .push((node.start_byte(), fragmented.class_range.end_byte));
4559            } else {
4560                // A macro-constrained member can make the full body reparse
4561                // unsafe while tree-sitter still exposes later class members
4562                // as bounded siblings up to the displaced `}`/`;`. Keep the
4563                // structurally proven class/base declaration and re-own those
4564                // sibling nodes under it. They retain their original parser
4565                // nodes and exact ranges; the close boundary comes solely from
4566                // `fragmented_class_body`.
4567                // Template wrappers put the escaped members beside the
4568                // template rather than beside its malformed declaration.
4569                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4570                    if candidate.start_byte() >= fragmented.reparse_end {
4571                        break;
4572                    }
4573                    if cpp_fragment_sibling_is_class_member(
4574                        candidate,
4575                        fragmented.reparse_end,
4576                        self.source,
4577                    ) {
4578                        self.recovered_class_sibling_scopes
4579                            .insert(candidate.id(), member_scope.clone());
4580                    }
4581                }
4582            }
4583            for item in displaced_namespace_items {
4584                self.recovered_class_sibling_scopes
4585                    .insert(item.id(), scope.clone());
4586            }
4587            stack.extend(class_stack);
4588            return;
4589        }
4590        if self.visit_folded_aggregate(node, scope) {
4591            return;
4592        }
4593        match node.kind() {
4594            "template_declaration" => {
4595                if let Some(recovered) =
4596                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
4597                {
4598                    let mut template_scope = scope.clone();
4599                    template_scope.template_signature =
4600                        cpp_template_signature(node, recovered.declaration_node, self.source);
4601                    template_scope.template_metadata =
4602                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4603                    let raw_supertypes =
4604                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
4605                    let mut class_stack = Vec::new();
4606                    let class_unit = self.visit_named_class_like_shape(
4607                        recovered.class_node,
4608                        recovered.name,
4609                        Some(recovered.body),
4610                        true,
4611                        Some(recovered.range),
4612                        raw_supertypes,
4613                        &template_scope,
4614                        &mut class_stack,
4615                        ancestry,
4616                    );
4617                    self.parsed.record_materialization(
4618                        MaterializationRecord::RecoveredDeclaration {
4619                            recovery: recovered.range,
4620                            unit: class_unit.clone(),
4621                        },
4622                    );
4623                    let member_scope = ScopeInfo {
4624                        package_name: template_scope.package_name.clone(),
4625                        module: template_scope.module.clone(),
4626                        class_unit: Some(class_unit.clone()),
4627                        template_signature: template_scope.template_signature.clone(),
4628                        template_metadata: None,
4629                        declarations_are_fields: true,
4630                        recovered_specialization_member_scope: recovered
4631                            .class_node
4632                            .child_by_field_name("name")
4633                            .is_some_and(|name| name.kind() == "template_type"),
4634                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4635                    };
4636                    for tail_member in recovered.tail_members.into_iter().rev() {
4637                        stack.push(CppWork::Node(CppNodeWork {
4638                            node: tail_member,
4639                            scope: member_scope.clone(),
4640                        }));
4641                    }
4642                    stack.extend(class_stack);
4643                    for sibling in recovered.member_siblings {
4644                        self.recovered_class_sibling_scopes
4645                            .insert(sibling.id(), member_scope.clone());
4646                    }
4647                    return;
4648                }
4649                for index in (0..node.named_child_count()).rev() {
4650                    let Some(child) = node.named_child(index) else {
4651                        continue;
4652                    };
4653                    if matches!(
4654                        child.kind(),
4655                        "class_specifier"
4656                            | "struct_specifier"
4657                            | "union_specifier"
4658                            | "enum_specifier"
4659                            | "function_definition"
4660                            | "declaration"
4661                            | "field_declaration"
4662                            | "alias_declaration"
4663                            | "namespace_definition"
4664                    ) {
4665                        let mut template_scope = scope.clone();
4666                        template_scope.template_signature =
4667                            cpp_template_signature(node, child, self.source);
4668                        template_scope.template_metadata =
4669                            cpp_template_metadata(node, child, self.source, ancestry);
4670                        if let Some(recovered) = recover_fragmented_partial_specialization(
4671                            node,
4672                            child,
4673                            self.source,
4674                            ancestry,
4675                        ) {
4676                            let code_unit = self.visit_named_class_like_shape(
4677                                recovered.declaration_node,
4678                                recovered.name,
4679                                None,
4680                                true,
4681                                Some(recovered.range),
4682                                None,
4683                                &template_scope,
4684                                stack,
4685                                ancestry,
4686                            );
4687                            self.parsed.record_materialization(
4688                                MaterializationRecord::RecoveredDeclaration {
4689                                    recovery: recovered.range,
4690                                    unit: code_unit.clone(),
4691                                },
4692                            );
4693                            let mut member_scope = template_scope.clone();
4694                            member_scope.class_unit = Some(code_unit);
4695                            member_scope.declarations_are_fields = true;
4696                            member_scope.recovered_specialization_member_scope = true;
4697                            for prefix_member in recovered.prefix_members.into_iter().rev() {
4698                                stack.push(CppWork::Node(CppNodeWork {
4699                                    node: prefix_member,
4700                                    scope: member_scope.clone(),
4701                                }));
4702                            }
4703                            for sibling in recovered.member_siblings {
4704                                self.recovered_class_sibling_scopes
4705                                    .insert(sibling.id(), member_scope.clone());
4706                            }
4707                            for following in recovered.following_declarations.into_iter().rev() {
4708                                stack.push(CppWork::Node(CppNodeWork {
4709                                    node: following,
4710                                    scope: scope.clone(),
4711                                }));
4712                            }
4713                            return;
4714                        }
4715                        stack.push(CppWork::Node(CppNodeWork {
4716                            node: child,
4717                            scope: template_scope,
4718                        }));
4719                    }
4720                }
4721            }
4722            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4723            "linkage_specification" => {
4724                if let Some(body) = cpp_body_node(node) {
4725                    stack.push(CppWork::Container(CppContainer {
4726                        node: body,
4727                        scope: scope.clone(),
4728                    }));
4729                } else {
4730                    stack.push(CppWork::Container(CppContainer {
4731                        node,
4732                        scope: scope.clone(),
4733                    }));
4734                }
4735            }
4736            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4737                self.visit_class_like(node, scope, stack, ancestry)
4738            }
4739            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4740            // A bare namespace-begin sentinel can make tree-sitter promote the
4741            // wrapped declaration to an ERROR node instead of the usual bogus
4742            // function_definition envelope. Keep the recovery entry point on
4743            // the same structured path for both shapes; ordinary ERROR nodes
4744            // retain their declaration-preserving wrapper traversal when the
4745            // sentinel predicate does not match.
4746            "ERROR" => {
4747                self.visit_object_macro_error_classes(node, scope);
4748                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4749                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4750                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4751                        return;
4752                    }
4753                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4754                        return;
4755                    }
4756                    self.visit_macro_swallowed_function_declarations(node, scope);
4757                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4758                    self.visit_stranded_class_members(node, scope, ancestry);
4759                    stack.push(CppWork::Container(CppContainer {
4760                        node,
4761                        scope: scope.clone(),
4762                    }));
4763                }
4764            }
4765            "declaration" => {
4766                if node.has_error() {
4767                    // Object-like macro lines before a function-like
4768                    // export-macro class head keep the head in a `declaration`
4769                    // rather than in an `ERROR` (#2924). It is the same head
4770                    // and the same following-sibling body, so it takes the same
4771                    // recovery; without it the head's macro invocation indexes
4772                    // as a field named after the macro.
4773                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4774                        return;
4775                    }
4776                    self.visit_prototype_macro_declarations(node, scope);
4777                    if self.node_is_inside_consumed_fragment(node) {
4778                        // The whole declaration was a recovered K&R
4779                        // prototype macro invocation (issue #2932); the
4780                        // reparse above already indexed its Function.
4781                        // Falling through to the ordinary declaration
4782                        // visitor would additionally mint the swapped-field
4783                        // wreckage this recovery exists to replace.
4784                        return;
4785                    }
4786                }
4787                if scope.class_unit.is_some()
4788                    && scope.declarations_are_fields
4789                    && scope.recovered_specialization_member_scope
4790                    && let Some(alias_name) =
4791                        recovered_using_declaration_alias_name(node, self.source)
4792                {
4793                    self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4794                } else {
4795                    self.visit_declaration(
4796                        node,
4797                        scope,
4798                        scope.declarations_are_fields,
4799                        stack,
4800                        ancestry,
4801                    )
4802                }
4803            }
4804            // A K&R prototype macro invocation with a pointer return type and a
4805            // simple argument list parses with no ERROR node at all: tree-sitter
4806            // reads `T *name _(( args ));` as a multiplication of a type by a
4807            // call (`T * name_(...)`), wrapped in an `expression_statement`
4808            // that `has_error()` only because of a `MISSING "::"` inside it
4809            // (issue #2932). Nothing else mints a declaration from an
4810            // `expression_statement` at declaration scope today, so there is
4811            // no existing behavior to preserve here if this node is not the
4812            // K&R shape; the admission gate inside
4813            // `visit_prototype_macro_declarations` (exactly one clean function
4814            // prototype spanning the recovered run) is what keeps this arm
4815            // safe on an ordinary errorful expression statement that happens
4816            // to contain nested parentheses.
4817            "expression_statement" => {
4818                if node.has_error() {
4819                    self.visit_prototype_macro_declarations(node, scope);
4820                }
4821            }
4822            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4823            "preproc_call" => self.visit_preproc_call(node, scope),
4824            "type_definition" | "alias_declaration" => {
4825                self.visit_type_declaration(node, scope, stack, ancestry)
4826            }
4827            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4828            // `#include` is collected by `collect_cpp_includes` before the
4829            // walk, so a directive the container walk never reaches -- inside
4830            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4831            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4832            // include claim.
4833            "preproc_include" => {}
4834            kind if preserves_declaration_scope_through_wrapper(
4835                kind,
4836                scope.class_unit.is_some(),
4837            ) =>
4838            {
4839                // A preprocessor conditional gates every declaration inside it
4840                // on a configuration this analyzer never evaluates; record the
4841                // interval so declaration state can say so (issue #1476). The
4842                // else/elif branches are children of the `preproc_if` node, so
4843                // recording the openers covers every branch.
4844                if kind == "labeled_statement" {
4845                    self.visit_access_label_constructor(node, scope);
4846                }
4847                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4848                    let mut range = cpp_declaration_range(node);
4849                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4850                        range.end_byte = boundary.end_byte;
4851                        range.end_line = boundary.end_line;
4852                    }
4853                    self.parsed.record_materialization(
4854                        MaterializationRecord::ConfigurationConditional { range },
4855                    );
4856                    if node.has_error() {
4857                        // A malformed export-macro class can close the namespace
4858                        // node early while the enclosing include guard still owns
4859                        // the remaining class-head/body pairs. The ordinary walk
4860                        // cannot carry the lost namespace through those promoted
4861                        // siblings. Scan only structured ERROR nodes in this
4862                        // already-malformed conditional; the pair recovery's
4863                        // exact class/macro/body predicate remains the admission
4864                        // gate, and its namespace lifting restores the owner.
4865                        let mut candidates = vec![node];
4866                        while let Some(candidate) = candidates.pop() {
4867                            // An `ERROR` still inside a namespace body is one
4868                            // the ordinary walk reaches with that namespace in
4869                            // scope. Claiming it here registers the recovered
4870                            // class at file scope and the consumed region then
4871                            // suppresses the walk that would have named it
4872                            // correctly -- which is why Botan's `DL_Group` was
4873                            // `DL_Group` and not `Botan.DL_Group` in the real
4874                            // header, where an include guard wraps the
4875                            // namespace, and was right in a fixture without one
4876                            // (#2552).
4877                            if candidate.kind() == "ERROR"
4878                                && !cpp_is_inside_namespace_body(candidate, ancestry)
4879                                && self.visit_function_like_export_class_pair(
4880                                    candidate, scope, stack, ancestry,
4881                                )
4882                            {
4883                                continue;
4884                            }
4885                            for index in (0..candidate.named_child_count()).rev() {
4886                                candidates.push(
4887                                    candidate
4888                                        .named_child(index)
4889                                        .expect("index below the node's own named child count"),
4890                                );
4891                            }
4892                        }
4893                    }
4894                }
4895                stack.push(CppWork::Container(CppContainer {
4896                    node,
4897                    scope: scope.clone(),
4898                }))
4899            }
4900            // A macro invocation the parser could not group leaves its head in
4901            // whatever slot was free. The `function_definition` and `ERROR`
4902            // arms above try the same recovery on the envelopes they get; a
4903            // flattened run leaves a bare `identifier`/`type_identifier`, and a
4904            // swallowed tail can land in a `parameter_declaration` (#3094).
4905            _ => {
4906                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
4907            }
4908        }
4909    }
4910
4911    fn visit_macro_swallowed_function_declarations<'tree>(
4912        &mut self,
4913        envelope: Node<'tree>,
4914        scope: &ScopeInfo,
4915    ) {
4916        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
4917            || envelope.kind() == "ERROR"
4918                && envelope
4919                    .parent()
4920                    .is_some_and(|parent| parent.kind() == "ERROR")
4921        {
4922            return;
4923        }
4924        let mut stack = (0..envelope.named_child_count())
4925            .filter_map(|index| envelope.named_child(index))
4926            .collect::<Vec<_>>();
4927        while let Some(node) = stack.pop() {
4928            if node.kind() == "function_declarator" {
4929                self.visit_error_swallowed_function_declaration(node, scope);
4930            }
4931            for child in named_children_iter(node) {
4932                stack.push(child);
4933            }
4934        }
4935    }
4936
4937    /// Index the declarations an attribute-like macro invocation swallowed into
4938    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
4939    /// the shape and why the parser produces it.
4940    fn visit_macro_wrapped_declarations<'tree>(
4941        &mut self,
4942        envelope: Node<'tree>,
4943        scope: &ScopeInfo,
4944        ancestry: &ParentIndex<'tree>,
4945    ) {
4946        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
4947        if recovered.is_empty() {
4948            return;
4949        }
4950        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
4951        self.record_recovered_declarations(recovery, |visitor| {
4952            for declaration in recovered {
4953                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
4954            }
4955        });
4956    }
4957
4958    /// Index the declarations a macro invocation collapsed. See
4959    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
4960    /// produces it. Returns whether it claimed the region beginning at
4961    /// `envelope`.
4962    ///
4963    /// The parser's own nodes cannot be read the way the swallowed tail of the
4964    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
4965    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
4966    /// with no declarator left at all, and the declarators that do survive on
4967    /// the envelope's declarator spine pair one declaration's name with the
4968    /// *next* declaration's parameter list. Reading those would be a guess.
4969    ///
4970    /// The bytes are still ordinary declarations, though, and the collapse is
4971    /// the parser carrying the failure forward from one macro invocation. So
4972    /// reparse the region, walk the items the parser makes of it, and when one
4973    /// item is itself a collapsed run, recover that invocation from its own
4974    /// bytes and resume the scan just past it. Each pass starts later than the
4975    /// last, so the scan is a loop over the invocations that collapse, not over
4976    /// the declarations: `llama.h` needs three passes for 214 declarations.
4977    ///
4978    /// The region can reach past `envelope` into the siblings the parser handed
4979    /// the rest of one invocation, so it is recorded as consumed: those
4980    /// siblings are this recovery's, and the ordinary walk must not read them
4981    /// again. Recording it after the scan, not before, keeps the scan's own
4982    /// reparsed nodes -- which carry their original byte offsets -- visible to
4983    /// the walk it drives.
4984    fn visit_collapsed_macro_declaration_run<'tree>(
4985        &mut self,
4986        envelope: Node<'tree>,
4987        scope: &ScopeInfo,
4988        ancestry: &ParentIndex<'tree>,
4989    ) -> bool {
4990        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
4991            return false;
4992        };
4993        let start = envelope.start_byte();
4994        let end = run.region_end;
4995        let recovery = cpp_recovery_window(self.source, start, end);
4996        self.record_recovered_declarations(recovery, |visitor| {
4997            let mut position = start;
4998            while position < end {
4999                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5000                    return;
5001                };
5002                let root = tree.root_node();
5003                // A region reparse is its own tree and needs its own parent
5004                // index; the caller's answers nothing about these nodes.
5005                let ancestry = ParentIndex::new(root);
5006                let mut cursor = root.walk();
5007                let collapsed =
5008                    root.named_children(&mut cursor)
5009                        .enumerate()
5010                        .find_map(|(index, item)| {
5011                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5012                                .map(|run| (index, item, run))
5013                        });
5014                // Walk only the items before the collapsed one. It swallowed
5015                // the rest of the region, so handing it to the ordinary walk
5016                // would re-enter this recovery on a region that starts where
5017                // this one did.
5018                let mut stack = Vec::new();
5019                push_cpp_sibling_range(
5020                    root,
5021                    0,
5022                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5023                    scope.clone(),
5024                    &mut stack,
5025                );
5026                visitor.drain_cpp_work(stack, &ancestry);
5027                let Some((_, item, run)) = collapsed else {
5028                    return;
5029                };
5030                // On its own bytes the invocation is the declaration-scope
5031                // shape `macro_wrapped_declarations` reads, so its wrapped
5032                // declaration needs no reader of its own here.
5033                if let Some(head) =
5034                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5035                {
5036                    let head_root = head.root_node();
5037                    visitor.run_container_work(
5038                        head_root,
5039                        scope.clone(),
5040                        &ParentIndex::new(head_root),
5041                    );
5042                }
5043                assert!(
5044                    run.invocation_end > position,
5045                    "a collapsed run at {position} must end after the byte the scan resumed \
5046                     from, but ended at {}",
5047                    run.invocation_end
5048                );
5049                position = run.invocation_end;
5050            }
5051        });
5052        self.consumed_fragment_regions.push((start, end));
5053        true
5054    }
5055
5056    /// Index the members a string-argument attribute macro stranded in an
5057    /// `ERROR` inside a class body.
5058    ///
5059    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
5060    /// grouping of the attributed member and of every member written after it
5061    /// until it recovers. The members are still whole `function_declarator`
5062    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
5063    /// export-macro class such as Botan's `DL_Group` was indexed with no
5064    /// members at all (#2552).
5065    fn visit_stranded_class_members<'tree>(
5066        &mut self,
5067        node: Node<'tree>,
5068        scope: &ScopeInfo,
5069        ancestry: &ParentIndex<'tree>,
5070    ) {
5071        if scope.class_unit.is_none() || !scope.declarations_are_fields {
5072            return;
5073        }
5074        for member in stranded_declaration_run(node, self.source).declarations {
5075            self.add_macro_wrapped_declaration(member, scope, ancestry);
5076        }
5077    }
5078
5079    /// Index the constructor an access label swallowed in a reparsed class
5080    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
5081    ///
5082    /// The declarator is recovered by reparsing from that call to the end of
5083    /// the label's own statement, which is the same offset-preserving region
5084    /// reparse every other recovery here uses, so the recovered nodes carry
5085    /// their true source positions.
5086    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5087        let Some(class_unit) = scope.class_unit.clone() else {
5088            return;
5089        };
5090        if !scope.declarations_are_fields {
5091            return;
5092        }
5093        let class_name = class_unit.identifier().to_string();
5094        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5095        else {
5096            return;
5097        };
5098        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5099            return;
5100        };
5101        let root = tree.root_node();
5102        let Some(declarator) =
5103            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5104        else {
5105            return;
5106        };
5107        let reparsed_ancestry = ParentIndex::new(root);
5108        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5109        let range = cpp_declaration_range(definition.unwrap_or(declarator));
5110        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5111        self.record_recovered_declarations(recovery, |visitor| {
5112            visitor.add_macro_wrapped_declaration(
5113                MacroWrappedDeclaration {
5114                    declarator,
5115                    range,
5116                    is_static: false,
5117                },
5118                scope,
5119                &reparsed_ancestry,
5120            );
5121        });
5122    }
5123
5124    fn add_macro_wrapped_declaration<'tree>(
5125        &mut self,
5126        declaration: MacroWrappedDeclaration<'tree>,
5127        scope: &ScopeInfo,
5128        ancestry: &ParentIndex<'tree>,
5129    ) {
5130        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5131        else {
5132            return;
5133        };
5134        let code_unit =
5135            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5136        if self.parsed.contains_declaration(&code_unit) {
5137            self.parsed
5138                .record_navigation_range(code_unit, declaration.range);
5139            return;
5140        }
5141        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5142        let signature = normalize_cpp_whitespace(
5143            self.source
5144                .get(declaration.range.start_byte..declaration.range.end_byte)
5145                .expect("a recovered declaration range covers one source range"),
5146        );
5147        let linkage = if declaration.is_static {
5148            CallableLinkage::Internal
5149        } else {
5150            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5151        };
5152        // Whether this is a definition is a property of the recovered node, not
5153        // of the caller: a declarator that a `function_definition` gives a body
5154        // is a definition wherever the recovery found it.
5155        let declaration_only =
5156            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5157        self.parsed.add_signature_with_metadata(
5158            code_unit.clone(),
5159            cpp_signature_metadata(signature, declaration.declarator, self.source, ancestry)
5160                .with_declaration_only(declaration_only)
5161                .with_callable_linkage(linkage),
5162        );
5163        if let Some(parent) = &scope.class_unit {
5164            self.parsed.add_child(parent.clone(), code_unit);
5165        } else if let Some(module) = &scope.module {
5166            self.parsed.add_child(module.clone(), code_unit);
5167        }
5168    }
5169
5170    /// Index only anonymous aggregate declarations from an ordinary C
5171    /// function body. Function bodies are otherwise outside the declaration
5172    /// walk, but their anonymous aggregate owners must exist so structured
5173    /// local binding inference can name their fields (#2994).
5174    fn visit_c_anonymous_local_aggregates_in_function<'tree>(
5175        &mut self,
5176        function: Node<'tree>,
5177        scope: &ScopeInfo,
5178        stack: &mut Vec<CppWork<'tree>>,
5179        ancestry: &ParentIndex<'tree>,
5180    ) {
5181        if !self.c_tag_semantics || scope.class_unit.is_some() {
5182            return;
5183        }
5184        let Some(body) = cpp_body_node(function) else {
5185            return;
5186        };
5187        let mut pending = vec![body];
5188        while let Some(node) = pending.pop() {
5189            if matches!(node.kind(), "function_definition" | "lambda_expression") {
5190                continue;
5191            }
5192            if node.kind() == "declaration"
5193                && self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5194            {
5195                continue;
5196            }
5197            if matches!(
5198                node.kind(),
5199                "class_specifier" | "struct_specifier" | "union_specifier"
5200            ) {
5201                continue;
5202            }
5203            let mut cursor = node.walk();
5204            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5205            children.reverse();
5206            pending.extend(children);
5207        }
5208    }
5209
5210    fn visit_error_swallowed_function_declaration<'tree>(
5211        &mut self,
5212        node: Node<'tree>,
5213        scope: &ScopeInfo,
5214    ) -> bool {
5215        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5216            return false;
5217        };
5218        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5219            return false;
5220        };
5221        let root = tree.root_node();
5222        let mut cursor = root.walk();
5223        let declarations = root
5224            .named_children(&mut cursor)
5225            .filter(|child| child.kind() != "comment")
5226            .collect::<Vec<_>>();
5227        let [declaration] = declarations.as_slice() else {
5228            return false;
5229        };
5230        if declaration.kind() != "declaration"
5231            || declaration.has_error()
5232            || declaration.start_byte() != start
5233            || declaration.end_byte() != end
5234        {
5235            return false;
5236        }
5237        let recovery = cpp_recovery_window(self.source, start, end);
5238        // The reparsed region is its own tree, so this walk indexes it itself.
5239        let reparsed_ancestry = ParentIndex::new(root);
5240        self.record_recovered_declarations(recovery, |visitor| {
5241            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5242        });
5243        true
5244    }
5245
5246    /// Recover a K&R-compatibility prototype macro invocation such as
5247    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
5248    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
5249    /// defined only in headers outside this workspace (ruby's own
5250    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
5251    /// `name _((args))` and turns each such line into a malformed
5252    /// `declaration` or pointer-expression statement. Before this
5253    /// recovery, the ordinary declaration visitor read that wreckage as a
5254    /// field with the type and name swapped (e.g. a field literally named
5255    /// `VALUE`) and indexed no prototype for the real name at all.
5256    ///
5257    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
5258    /// preserve the declared name, a known one-argument compatibility macro,
5259    /// the nested argument list, and a live terminator. It rejects arbitrary
5260    /// identifiers and bare `ERROR` envelopes that flattened those relations;
5261    /// guessing there previously turned malformed non-macro declarations into
5262    /// invented functions. For each proven candidate this reparses the range
5263    /// before the macro token, the inner parenthesized argument node, and the
5264    /// terminating `;` as one included-range parse. That is the parser-native
5265    /// equivalent of expanding `#define _(args) args` while retaining original
5266    /// byte offsets. The result is admitted only when it is exactly one clean
5267    /// function prototype spanning the recovered run.
5268    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5269        for candidate in cpp_prototype_macro_candidates(node, self.source) {
5270            let start = candidate.run_start;
5271            let end = candidate.semicolon_end;
5272            if self.byte_range_is_inside_consumed_fragment(start, end) {
5273                continue;
5274            }
5275            let Some(tree) = parse_source_ranges_with_cancellation(
5276                &tree_sitter_cpp::LANGUAGE.into(),
5277                self.source,
5278                &candidate.ranges(),
5279                None,
5280            ) else {
5281                continue;
5282            };
5283            let root = tree.root_node();
5284            let mut cursor = root.walk();
5285            let declarations = root
5286                .named_children(&mut cursor)
5287                .filter(|child| child.kind() != "comment")
5288                .collect::<Vec<_>>();
5289            let [declaration] = declarations.as_slice() else {
5290                continue;
5291            };
5292            if declaration.kind() != "declaration"
5293                || declaration.has_error()
5294                || declaration.start_byte() != start
5295                || declaration.end_byte() != end
5296                || declaration
5297                    .child_by_field_name("declarator")
5298                    .and_then(extract_function_declarator)
5299                    .is_none()
5300            {
5301                continue;
5302            }
5303            let recovery = cpp_recovery_window(self.source, start, end);
5304            // The reparsed region is its own tree, so this walk indexes it
5305            // itself.
5306            let reparsed_ancestry = ParentIndex::new(root);
5307            self.record_recovered_declarations(recovery, |visitor| {
5308                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5309            });
5310            self.consumed_fragment_regions.push((start, end));
5311        }
5312    }
5313
5314    /// Declare one Module per namespace level of `components` under
5315    /// `package_name`, and return the innermost level's package name and
5316    /// Module. A level an earlier definition already declared is reused.
5317    fn declare_namespace_levels(
5318        &mut self,
5319        mut package_name: String,
5320        components: Vec<String>,
5321        node: Node<'_>,
5322    ) -> (String, Option<CodeUnit>) {
5323        let mut module = None;
5324        for component in components {
5325            let full_name = if package_name.is_empty() {
5326                component
5327            } else {
5328                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5329            };
5330            let level = CodeUnit::new_fq(
5331                self.file.clone(),
5332                CodeUnitType::Module,
5333                "",
5334                full_name.clone(),
5335                cpp_namespace_fq(&full_name),
5336            );
5337            if !self.parsed.contains_declaration(&level) {
5338                self.add_declaration(level.clone(), node, None, None);
5339            }
5340            package_name = full_name;
5341            module = Some(level);
5342        }
5343        (package_name, module)
5344    }
5345
5346    /// Restore the complete lexical namespace path when recovery dropped an
5347    /// ancestor or retained it past its real close. Parsed namespaces beginning
5348    /// inside the recovered region still contribute their normal nesting.
5349    /// Crossing that boundary also clears any malformed class owner.
5350    fn recovered_namespace_scope(
5351        &mut self,
5352        node: Node<'_>,
5353        scope: &ScopeInfo,
5354    ) -> Option<ScopeInfo> {
5355        self.orphaned_namespaces.region_at(node.start_byte())?;
5356        let components = self
5357            .orphaned_namespaces
5358            .enclosing_namespace_components(node, self.source);
5359        let package_name = components.join(CPP_PACKAGE_SEPARATOR);
5360        if package_name == scope.package_name {
5361            return None;
5362        }
5363        let (package_name, module) = self.declare_namespace_levels(String::new(), components, node);
5364        Some(ScopeInfo {
5365            package_name,
5366            module,
5367            // A recovered namespace body is no longer inside the class (or
5368            // function) that tree-sitter accidentally used as its wrapper.
5369            // Keep namespace/module context, but never carry that owner into
5370            // declarations published from the recovered region.
5371            class_unit: None,
5372            template_signature: None,
5373            template_metadata: None,
5374            declarations_are_fields: false,
5375            recovered_specialization_member_scope: false,
5376            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5377        })
5378    }
5379
5380    fn visit_namespace<'tree>(
5381        &mut self,
5382        node: Node<'tree>,
5383        scope: &ScopeInfo,
5384        stack: &mut Vec<CppWork<'tree>>,
5385        ancestry: &ParentIndex<'tree>,
5386    ) {
5387        let name_node = node.child_by_field_name("name");
5388        let Some(name_node) = name_node else {
5389            if let Some(body) = cpp_body_node(node) {
5390                stack.push(CppWork::Container(CppContainer {
5391                    node: body,
5392                    scope: scope.clone(),
5393                }));
5394            }
5395            return;
5396        };
5397        // Diagnostic corpora contain deliberately ill-formed global namespace
5398        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
5399        // the leading global `::` as the first anonymous child. Honor that AST
5400        // boundary instead of appending the name to the lexical namespace;
5401        // appending produced legacy names such as `outer::::outer::inner`, which
5402        // could not round-trip through the structured FqName boundary.
5403        let explicitly_global = name_node
5404            .child(0)
5405            .is_some_and(|child| !child.is_named() && child.kind() == "::");
5406        let components = cpp_namespace_name_components(name_node, self.source);
5407        if components.is_empty() {
5408            return;
5409        }
5410        // One Module per namespace level. C++17's `namespace a::b { ... }` is
5411        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
5412        // shorthand must declare `a` as well as `a::b` -- extracting only the
5413        // innermost level left the enclosing namespace undeclared and made the
5414        // two spellings of one construct disagree (issue #1878).
5415        let package_name = if explicitly_global {
5416            String::new()
5417        } else {
5418            scope.package_name.clone()
5419        };
5420        let (package_name, module) = self.declare_namespace_levels(package_name, components, node);
5421
5422        let namespace_scope = ScopeInfo {
5423            package_name,
5424            module,
5425            // C++ never nests a namespace inside a class, so a surviving
5426            // class_unit here is always recovery bleed: a malformed-region
5427            // boundary upstream mis-scoped this namespace block. Keeping the
5428            // owner would mint the namespace's declarations as class members
5429            // under a re-appended package, desyncing the fq boundary assert
5430            // (#2306). Dropping it is identity-neutral for valid code, where
5431            // class_unit is always empty at a namespace definition.
5432            class_unit: None,
5433            template_signature: scope.template_signature.clone(),
5434            template_metadata: scope.template_metadata.clone(),
5435            declarations_are_fields: false,
5436            recovered_specialization_member_scope: false,
5437            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5438        };
5439        let container = cpp_body_node(node).unwrap_or(node);
5440        // A malformed export-macro class body may turn the following class
5441        // into a descendant of a bogus function/labeled/error envelope. Those
5442        // descendants are not declaration containers and the ordinary walk
5443        // intentionally does not descend into them. Scan the namespace tree
5444        // once for the strict embedded class geometry before scheduling its
5445        // normal declarations. When one envelope matches, its helper recovers
5446        // every embedded class and the walk need not inspect its descendants.
5447        let mut candidates = vec![container];
5448        while let Some(candidate) = candidates.pop() {
5449            if matches!(
5450                candidate.kind(),
5451                "ERROR" | "function_definition" | "labeled_statement"
5452            ) && self.visit_embedded_function_like_export_classes(
5453                candidate,
5454                &namespace_scope,
5455                stack,
5456                ancestry,
5457            ) {
5458                continue;
5459            }
5460            for index in (0..candidate.named_child_count()).rev() {
5461                candidates.push(
5462                    candidate
5463                        .named_child(index)
5464                        .expect("index below the node's own named child count"),
5465                );
5466            }
5467        }
5468        stack.push(CppWork::Container(CppContainer {
5469            node: container,
5470            scope: namespace_scope,
5471        }));
5472    }
5473
5474    fn visit_class_like<'tree>(
5475        &mut self,
5476        node: Node<'tree>,
5477        scope: &ScopeInfo,
5478        stack: &mut Vec<CppWork<'tree>>,
5479        ancestry: &ParentIndex<'tree>,
5480    ) {
5481        let Some(name) = class_like_name(node, self.source, ancestry) else {
5482            return;
5483        };
5484        let name = qualified_class_name_chain(node, self.source, scope)
5485            .map(|chain| chain.join("$"))
5486            .unwrap_or(name);
5487        self.visit_named_class_like(node, name, scope, stack, ancestry);
5488    }
5489
5490    fn visit_named_class_like<'tree>(
5491        &mut self,
5492        node: Node<'tree>,
5493        name: String,
5494        scope: &ScopeInfo,
5495        stack: &mut Vec<CppWork<'tree>>,
5496        ancestry: &ParentIndex<'tree>,
5497    ) {
5498        let body = cpp_body_node(node);
5499        let definition_body_present = body.is_some();
5500        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5501            .then(|| extract_cpp_supertypes(node, self.source));
5502        self.visit_named_class_like_shape(
5503            node,
5504            name,
5505            body,
5506            definition_body_present,
5507            None,
5508            raw_supertypes,
5509            scope,
5510            stack,
5511            ancestry,
5512        );
5513    }
5514
5515    /// Whether this class-like declaration is a C tag that belongs to the
5516    /// enclosing non-aggregate scope rather than to the aggregate it is
5517    /// lexically written inside.
5518    ///
5519    /// `class_specifier` is deliberately excluded: `class` is not C, so text
5520    /// that spells one in a `.c` file is not C code and keeps the C++ reading
5521    /// rather than getting a half-C identity.
5522    fn mints_tag_at_enclosing_c_scope(
5523        &self,
5524        declaration_node: Node<'_>,
5525        scope: &ScopeInfo,
5526        ancestry: &ParentIndex<'_>,
5527    ) -> bool {
5528        self.c_tag_semantics
5529            && scope.class_unit.is_some()
5530            && class_like_name(declaration_node, self.source, ancestry).is_some()
5531            && matches!(
5532                declaration_node.kind(),
5533                "struct_specifier" | "union_specifier" | "enum_specifier"
5534            )
5535    }
5536
5537    #[allow(clippy::too_many_arguments)]
5538    fn visit_named_class_like_shape<'tree>(
5539        &mut self,
5540        declaration_node: Node<'tree>,
5541        name: String,
5542        body: Option<Node<'tree>>,
5543        definition_body_present: bool,
5544        explicit_range: Option<Range>,
5545        raw_supertypes: Option<Vec<String>>,
5546        scope: &ScopeInfo,
5547        stack: &mut Vec<CppWork<'tree>>,
5548        ancestry: &ParentIndex<'tree>,
5549    ) -> CodeUnit {
5550        let displaced_macro_tail = if explicit_range.is_none() {
5551            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5552        } else {
5553            None
5554        };
5555        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5556        let recovered_scope = self.scope_for_recovered_exported_class(
5557            declaration_node,
5558            &name,
5559            definition_body_present,
5560            scope,
5561            ancestry,
5562        );
5563        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
5564        // aggregate's member list is declared at the enclosing non-aggregate
5565        // scope, not nested inside the aggregate. `scope.class_unit` is the
5566        // only aggregate carrier in this walk, so dropping it puts the tag at
5567        // the nearest enclosing non-aggregate scope -- the module at file or
5568        // namespace scope, and the same block-scope representation a
5569        // function-local aggregate already gets. The tag's own body scope
5570        // below still owns its members, so fields and enumerators are
5571        // unaffected.
5572        let c_tag_scope;
5573        let scope =
5574            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
5575                c_tag_scope = ScopeInfo {
5576                    class_unit: None,
5577                    ..recovered_scope.clone()
5578                };
5579                &c_tag_scope
5580            } else {
5581                &recovered_scope
5582            };
5583        let short_name = if let Some(parent) = &scope.class_unit {
5584            cpp_join_nested_short(parent.short_name(), &name)
5585        } else {
5586            name.clone()
5587        };
5588        // A top-level out-of-line qualified class definition (`struct
5589        // Outer::Inner { ... }` inside its namespace, #2246) carries its
5590        // nesting chain as the `$`-joined display name; push one Type/Nested
5591        // segment per class so segment-pop owner navigation keeps working.
5592        // Every other leaf name stays opaque so a literal `$` in a source
5593        // identifier never crosses the split/join boundary (#2140).
5594        let qualified_chain = if scope.class_unit.is_none() {
5595            qualified_class_name_chain(declaration_node, self.source, scope)
5596                .filter(|chain| chain.join("$") == name)
5597        } else {
5598            None
5599        };
5600        let fq = if let Some(chain) = qualified_chain {
5601            let mut fq = FqName::new();
5602            cpp_push_package(&mut fq, &scope.package_name);
5603            let mut first = true;
5604            for component in chain {
5605                let kind = if first {
5606                    SegmentKind::Type
5607                } else {
5608                    SegmentKind::Nested
5609                };
5610                fq.push(cpp_segment(&component, kind));
5611                first = false;
5612            }
5613            fq
5614        } else {
5615            cpp_leaf_fq(
5616                &scope.package_name,
5617                scope.class_unit.as_ref(),
5618                &name,
5619                SegmentKind::Nested,
5620                SegmentKind::Type,
5621            )
5622        };
5623        let code_unit = CodeUnit::with_signature_and_fq(
5624            self.file.clone(),
5625            CodeUnitType::Class,
5626            scope.package_name.clone(),
5627            short_name,
5628            scope.template_signature.clone(),
5629            false,
5630            fq,
5631        );
5632        let has_body = definition_body_present;
5633        if !has_body && self.parsed.contains_declaration(&code_unit) {
5634            self.parsed.record_navigation_range(
5635                code_unit.clone(),
5636                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5637            );
5638            return code_unit;
5639        }
5640        if has_body {
5641            if let Some(range) = explicit_range {
5642                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5643            } else {
5644                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5645            }
5646        } else {
5647            self.add_declaration(code_unit.clone(), declaration_node, None, None);
5648        }
5649        if let Some(raw_supertypes) = raw_supertypes {
5650            self.parsed
5651                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5652        }
5653        self.parsed.add_signature(
5654            code_unit.clone(),
5655            render_cpp_type_signature(
5656                declaration_node,
5657                self.source,
5658                scope.template_signature.as_deref(),
5659            ),
5660        );
5661        if let Some(metadata) = &scope.template_metadata {
5662            let primary_short_name = if let Some(parent) = &scope.class_unit {
5663                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5664            } else {
5665                metadata.primary_name.clone()
5666            };
5667            let primary_fq_name = CodeUnit::new(
5668                self.file.clone(),
5669                CodeUnitType::Class,
5670                scope.package_name.clone(),
5671                primary_short_name,
5672            )
5673            .fq_name();
5674            let mut metadata = metadata.clone();
5675            metadata.primary_fq_name = primary_fq_name;
5676            self.parsed
5677                .set_cpp_template_metadata(code_unit.clone(), metadata);
5678        }
5679        if let Some(parent) = &scope.class_unit {
5680            self.parsed.add_child(parent.clone(), code_unit.clone());
5681        } else if let Some(module) = &scope.module {
5682            self.parsed.add_child(module.clone(), code_unit.clone());
5683        }
5684
5685        if let Some(body) = body {
5686            let mut nested_scope = scope.clone();
5687            nested_scope.class_unit = Some(code_unit.clone());
5688            nested_scope.template_signature = scope.template_signature.clone();
5689            // Template metadata describes the class just created. It must not
5690            // leak into ordinary nested declarations in that class's body.
5691            // Recovered export-macro specializations carry a separate scope bit
5692            // for their declaration-shaped body members.
5693            nested_scope.template_metadata = None;
5694            // Export-macro class bodies recovered from a function_definition use
5695            // compound_statement children, whose direct fields are declarations.
5696            nested_scope.recovered_specialization_member_scope =
5697                scope.template_metadata.as_ref().is_some_and(|metadata| {
5698                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
5699                });
5700            nested_scope.declarations_are_fields =
5701                is_recovered_exported_class_container(declaration_node, self.source)
5702                    || nested_scope.recovered_specialization_member_scope;
5703            if let Some(displaced) = displaced_macro_tail {
5704                // A macro-shaped field without a source semicolon can make
5705                // tree-sitter consume the real class terminator as an ERROR
5706                // inside that field, then retain following namespace items as
5707                // later field-list children. Drain the proven class prefix
5708                // first and re-own only the structured tail with the outer
5709                // scope. The tail is pushed first because the work stack is
5710                // LIFO.
5711                push_cpp_sibling_range(
5712                    body,
5713                    displaced.split_index,
5714                    usize::MAX,
5715                    scope.clone(),
5716                    stack,
5717                );
5718                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5719            } else {
5720                stack.push(CppWork::Container(CppContainer {
5721                    node: body,
5722                    scope: nested_scope,
5723                }));
5724            }
5725        }
5726        if declaration_node.kind() == "enum_specifier" {
5727            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5728            if !self.has_enum_enumerator_units(&code_unit) {
5729                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5730            }
5731        }
5732        code_unit
5733    }
5734
5735    /// Whether the parse product already holds enumerator fields for `parent`,
5736    /// answered from the walk's carried-forward field ownership index.
5737    ///
5738    /// Built on the first enum's question and advanced by every declaration
5739    /// recorded after it, so a file that declares no enum -- most files -- pays
5740    /// nothing, and one that declares thousands pays a single pass instead of
5741    /// one per enum (#2786).
5742    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5743        if self.field_owners.is_none() {
5744            self.field_owners = Some(CppFieldOwnerIndex::of(
5745                self.parsed.declarations().iter(),
5746                self.file,
5747            ));
5748        }
5749        debug_assert_eq!(
5750            parent.source(),
5751            self.file,
5752            "the walk's declarations are declarations of the file it is walking"
5753        );
5754        let carried = self
5755            .field_owners
5756            .as_ref()
5757            .expect("the index was just ensured")
5758            .owns_fields(parent.package_name(), parent.short_name());
5759
5760        #[cfg(debug_assertions)]
5761        assert_eq!(
5762            carried,
5763            cpp_declarations_hold_owned_fields(
5764                self.parsed.declarations(),
5765                self.file,
5766                parent.package_name(),
5767                parent.short_name()
5768            ),
5769            "the carried-forward field index must answer what a fresh declaration scan \
5770             answers for {}",
5771            parent.fq_name()
5772        );
5773
5774        carried
5775    }
5776
5777    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5778        walk_named_tree_preorder(node, false, |child| {
5779            if child.kind() != "enumerator" {
5780                return WalkControl::Continue;
5781            }
5782            let Some(name_node) = child.child_by_field_name("name") else {
5783                return WalkControl::Continue;
5784            };
5785            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5786            if name.is_empty() {
5787                return WalkControl::Continue;
5788            }
5789            let code_unit = CodeUnit::new_fq(
5790                self.file.clone(),
5791                CodeUnitType::Field,
5792                scope.package_name.clone(),
5793                cpp_join_member_short(parent.short_name(), &name),
5794                parent
5795                    .fq()
5796                    .clone()
5797                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5798            );
5799            if self.parsed.contains_declaration(&code_unit) {
5800                return WalkControl::Continue;
5801            }
5802            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5803            self.parsed.add_signature(
5804                code_unit,
5805                normalize_cpp_whitespace(node_text(child, self.source)),
5806            );
5807            WalkControl::Continue
5808        });
5809    }
5810
5811    fn visit_enum_enumerators_from_text(
5812        &mut self,
5813        node: Node<'_>,
5814        scope: &ScopeInfo,
5815        parent: &CodeUnit,
5816    ) {
5817        let text = node_text(node, self.source);
5818        let Some((_, body)) = text.split_once('{') else {
5819            return;
5820        };
5821        let Some((body, _)) = body.rsplit_once('}') else {
5822            return;
5823        };
5824        for entry in body.split(',') {
5825            let trimmed = entry.trim();
5826            let name = trimmed
5827                .split('=')
5828                .next()
5829                .unwrap_or("")
5830                .split_whitespace()
5831                .next()
5832                .unwrap_or("");
5833            if name.is_empty() {
5834                continue;
5835            }
5836            let code_unit = CodeUnit::new_fq(
5837                self.file.clone(),
5838                CodeUnitType::Field,
5839                scope.package_name.clone(),
5840                cpp_join_member_short(parent.short_name(), name),
5841                parent
5842                    .fq()
5843                    .clone()
5844                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
5845            );
5846            if self.parsed.contains_declaration(&code_unit) {
5847                continue;
5848            }
5849            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
5850            self.parsed.add_signature(code_unit, trimmed.to_string());
5851        }
5852    }
5853
5854    fn visit_function_definition<'tree>(
5855        &mut self,
5856        node: Node<'tree>,
5857        scope: &ScopeInfo,
5858        stack: &mut Vec<CppWork<'tree>>,
5859        ancestry: &ParentIndex<'tree>,
5860    ) {
5861        // An attribute-like macro invocation whose argument is a declaration can
5862        // swallow every declaration written after it into one bogus
5863        // `function_definition` (#2551). This owns the whole region, so it runs
5864        // ahead of `visit_macro_swallowed_function_declarations` below, which
5865        // admits the same envelope by its head macro token and reads single
5866        // declarators out of nodes this recovery reparses properly.
5867        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
5868            return;
5869        }
5870        // A file-scope object-like macro sentinel the parser cannot see (issue
5871        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
5872        // prefixes as a bogus `function_definition` that swallows real namespaces,
5873        // classes, and members. Reparse the swallowed interior as C++ items so the
5874        // ordinary declaration visitors index it with byte/line-exact ownership.
5875        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
5876            return;
5877        }
5878        if node.has_error() {
5879            self.visit_macro_swallowed_function_declarations(node, scope);
5880        }
5881        if let Some((class_node, name, raw_supertypes)) =
5882            recover_exported_class_function_definition(node, self.source)
5883        {
5884            if let Some(body) = cpp_body_node(node)
5885                && let Some(close) = self
5886                    .orphaned_namespaces
5887                    .matching_close_brace(body.start_byte())
5888                && close.end_byte < body.end_byte()
5889            {
5890                let class_range = Range {
5891                    start_byte: node.start_byte(),
5892                    end_byte: close.end_byte,
5893                    start_line: node.start_position().row + 1,
5894                    end_line: close.end_line,
5895                };
5896                // Parse the body in class context, preserving the original
5897                // keyword, recovered name and body as included ranges. Parsing
5898                // the interior at file scope turns constructors and access
5899                // labels into another oversized ERROR instead of members.
5900                let mut head = vec![node];
5901                let mut keyword = None;
5902                let mut name_node = None;
5903                while let Some(part) = head.pop() {
5904                    if part.start_byte() >= body.start_byte() || part.is_missing() {
5905                        continue;
5906                    }
5907                    if matches!(part.kind(), "class" | "struct" | "union") {
5908                        keyword = Some(part);
5909                    }
5910                    if matches!(part.kind(), "identifier" | "type_identifier")
5911                        && node_text(part, self.source) == name
5912                    {
5913                        name_node = Some(part);
5914                    }
5915                    let mut cursor = part.walk();
5916                    head.extend(part.children(&mut cursor));
5917                }
5918                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
5919                    && let Some(type_name) = keyword
5920                        .parent()
5921                        .and_then(|parent| parent.child_by_field_name("name"))
5922                    && let Some(tree) = parse_source_ranges_with_cancellation(
5923                        &tree_sitter_cpp::LANGUAGE.into(),
5924                        self.source,
5925                        &[
5926                            (keyword.start_byte(), type_name.start_byte()),
5927                            (name_node.start_byte(), name_node.end_byte()),
5928                            (body.start_byte(), close.end_byte),
5929                        ],
5930                        None,
5931                    )
5932                    && let Some(reparsed_class) = tree.root_node().named_child(0)
5933                    && let Some(class_body) = cpp_body_node(reparsed_class)
5934                    && class_body.start_byte() == body.start_byte()
5935                    && class_body.end_byte() == close.end_byte
5936                    && let Some(tail) =
5937                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
5938                {
5939                    let class_unit = self.visit_named_class_like_shape(
5940                        class_node,
5941                        name,
5942                        None,
5943                        true,
5944                        Some(class_range),
5945                        raw_supertypes,
5946                        scope,
5947                        stack,
5948                        ancestry,
5949                    );
5950                    self.parsed.record_materialization(
5951                        MaterializationRecord::RecoveredDeclaration {
5952                            recovery: class_range,
5953                            unit: class_unit.clone(),
5954                        },
5955                    );
5956                    let member_scope = ScopeInfo {
5957                        package_name: class_unit.package_name().to_string(),
5958                        class_unit: Some(class_unit),
5959                        declarations_are_fields: true,
5960                        template_metadata: None,
5961                        recovered_specialization_member_scope: false,
5962                        ..scope.clone()
5963                    };
5964                    let class_body_range = class_body.byte_range();
5965                    let tail_range = tail.root_node().byte_range();
5966                    self.partitioned_regions
5967                        .push((tail, tail_range, scope.clone()));
5968                    self.partitioned_regions
5969                        .push((tree, class_body_range, member_scope));
5970                    return;
5971                }
5972            }
5973            let body = cpp_body_node(class_node);
5974            let displaced_namespace = cpp_body_node(node)
5975                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
5976            let fragmented = cpp_body_node(node).and_then(|body| {
5977                fragmented_export_function_body_region(
5978                    node,
5979                    body,
5980                    self.source,
5981                    displaced_namespace.as_ref(),
5982                )
5983            });
5984            // The recovery tuple's first node is the class-like type when the
5985            // parser exposes one, but the synthetic wrapper owns the compound
5986            // statement that contains the truncated class body. Use the
5987            // wrapper body for fragmented-member detection; retain the
5988            // class-node body for the ordinary (non-fragmented) path below.
5989            if let Some(fragmented) = fragmented {
5990                // The lifted sibling no longer sits below the parser-visible
5991                // namespace node. Restore the current parent scope when the
5992                // ordinary work walk reaches that class.
5993                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
5994                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
5995                {
5996                    let mut boundary_scope = scope.clone();
5997                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
5998                        if sibling.start_byte() >= boundary.start_byte() {
5999                            break;
6000                        }
6001                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6002                            boundary_scope.visible_using_namespaces.push(namespace);
6003                        }
6004                    }
6005                    self.recovered_class_sibling_scopes
6006                        .insert(boundary.id(), boundary_scope);
6007                }
6008                let mut recovered_constructor = None;
6009                let mut recovered_prefix_tree = None;
6010                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6011                {
6012                    Some(FragmentedExportMembers::Complete(tree)) => {
6013                        if let Some(body) = body
6014                            && let Some(range) =
6015                                cpp_reparsed_synthetic_initializer_constructor_range(
6016                                    tree.root_node(),
6017                                    &name,
6018                                    self.source,
6019                                    body.end_byte(),
6020                                )
6021                        {
6022                            recovered_constructor = Some(range);
6023                            recovered_prefix_tree = Some(tree);
6024                            None
6025                        } else {
6026                            Some(FragmentedExportMembers::Complete(tree))
6027                        }
6028                    }
6029                    outcome => outcome,
6030                };
6031                let mut class_stack = Vec::new();
6032                let class_unit = self.visit_named_class_like_shape(
6033                    class_node,
6034                    name,
6035                    None,
6036                    true,
6037                    Some(fragmented.class_range),
6038                    raw_supertypes,
6039                    scope,
6040                    &mut class_stack,
6041                    ancestry,
6042                );
6043                self.parsed
6044                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6045                        recovery: fragmented.class_range,
6046                        unit: class_unit.clone(),
6047                    });
6048                let complete = outcome.is_some_and(|outcome| {
6049                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6050                });
6051                if complete {
6052                    self.consumed_fragment_regions
6053                        .push((node.start_byte(), fragmented.class_range.end_byte));
6054                } else {
6055                    // The reparse can fail when the first constructor or a
6056                    // method body is split into statement-shaped siblings.
6057                    // Keep the recovered class envelope, but do not visit the
6058                    // synthetic wrapper body: its initializer expressions can
6059                    // look like same-named member functions (for example
6060                    // `Token.location(loc)`). Re-own only the original sibling
6061                    // nodes that fall inside the proven class range. Their CST
6062                    // shapes retain the real field/function kinds and ranges.
6063                    let member_scope = ScopeInfo {
6064                        package_name: class_unit.package_name().to_string(),
6065                        module: scope.module.clone(),
6066                        class_unit: Some(class_unit.clone()),
6067                        template_signature: scope.template_signature.clone(),
6068                        template_metadata: None,
6069                        declarations_are_fields: true,
6070                        recovered_specialization_member_scope: false,
6071                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
6072                    };
6073                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6074                        if candidate.start_byte() >= fragmented.reparse_end {
6075                            break;
6076                        }
6077                        if cpp_fragment_sibling_is_class_member(
6078                            candidate,
6079                            fragmented.reparse_end,
6080                            self.source,
6081                        ) {
6082                            self.recovered_class_sibling_scopes
6083                                .insert(candidate.id(), member_scope.clone());
6084                        }
6085                    }
6086                    if let Some(range) = recovered_constructor
6087                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6088                    {
6089                        self.visit_recovered_fragment_prefix_members(
6090                            prefix_tree.root_node(),
6091                            range.start,
6092                            &class_unit,
6093                            scope,
6094                            ancestry,
6095                        );
6096                        self.visit_recovered_fragment_constructor(
6097                            range,
6098                            body,
6099                            class_node,
6100                            &class_unit,
6101                            scope,
6102                            ancestry,
6103                        );
6104                    }
6105                }
6106                if let Some(boundary) = displaced_namespace {
6107                    for item in boundary.namespace_items {
6108                        self.recovered_class_sibling_scopes
6109                            .insert(item.id(), scope.clone());
6110                    }
6111                }
6112                stack.extend(class_stack);
6113                return;
6114            }
6115            let mut stack = Vec::new();
6116            let class_unit = self.visit_named_class_like_shape(
6117                class_node,
6118                name,
6119                body,
6120                body.is_some(),
6121                None,
6122                raw_supertypes,
6123                scope,
6124                &mut stack,
6125                ancestry,
6126            );
6127            self.parsed
6128                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6129                    recovery: cpp_declaration_range(node),
6130                    unit: class_unit,
6131                });
6132            // Issue #1524: the bogus `function_definition` body can run past
6133            // the class's true closing brace (the parse ends it with a
6134            // zero-width `MISSING "}"`), swallowing following namespace-scope
6135            // siblings -- they would index as members of the recovered class.
6136            // When the body's text-balanced close lands before the body's own
6137            // end, re-own the swallowed tail with the outer scope instead.
6138            if let Some(body) = body
6139                && let Some(class_close) = self
6140                    .orphaned_namespaces
6141                    .matching_close_brace(body.start_byte())
6142                && class_close.start_byte < body.end_byte()
6143            {
6144                let split = {
6145                    let mut cursor = body.walk();
6146                    body.named_children(&mut cursor)
6147                        .position(|child| child.start_byte() > class_close.start_byte)
6148                };
6149                if let Some(split) = split {
6150                    // The seeded work is a single Container over the whole
6151                    // body with the class scope; replace it with the bounded
6152                    // head (class scope) plus the swallowed tail (outer
6153                    // scope). Push tail first so the head drains first.
6154                    let seeded = stack.pop();
6155                    match seeded {
6156                        Some(CppWork::Container(container)) => {
6157                            push_cpp_sibling_range(
6158                                body,
6159                                split,
6160                                usize::MAX,
6161                                scope.clone(),
6162                                &mut stack,
6163                            );
6164                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6165                        }
6166                        // visit_named_class_like_shape always seeds exactly
6167                        // one Container when a body is present.
6168                        _ => unreachable!("exported-class seed is always one Container"),
6169                    }
6170                }
6171            }
6172            while let Some(work) = stack.pop() {
6173                match work {
6174                    CppWork::Container(container) => {
6175                        push_cpp_container_work(container.node, container.scope, &mut stack);
6176                    }
6177                    CppWork::Siblings(siblings) => {
6178                        advance_cpp_siblings(siblings, self.source, &mut stack);
6179                    }
6180                    CppWork::Node(work) => {
6181                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6182                    }
6183                }
6184            }
6185            return;
6186        }
6187        let recovered_constraint_constructor =
6188            cpp_recovered_template_macro_constructor(node, self.source);
6189        let declarator = recovered_constraint_constructor
6190            .map(|(declarator, _)| declarator)
6191            .or_else(|| node.child_by_field_name("declarator"));
6192        let Some(declarator) = declarator else {
6193            self.visit_malformed_function_definition_container(node, scope, stack);
6194            return;
6195        };
6196        let Some(function_declarator) = extract_function_declarator(declarator) else {
6197            self.visit_malformed_function_definition_container(node, scope, stack);
6198            return;
6199        };
6200        let function = if let Some((_, callable_name)) =
6201            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6202        {
6203            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6204        } else {
6205            extract_function_info(function_declarator, self.source, scope)
6206        };
6207        let Some(mut function) = function else {
6208            self.visit_malformed_function_definition_container(node, scope, stack);
6209            return;
6210        };
6211        if let Some((_, template_parameter)) = recovered_constraint_constructor {
6212            function.signature = format!(
6213                "template <{}>{}",
6214                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6215                function.signature
6216            );
6217        }
6218        let code_unit = function.code_unit(self.file.clone());
6219        // Keep an earlier same-file prototype as another physical occurrence
6220        // of this callable. `CodeUnit` already identifies the role-neutral
6221        // overload, while ranges and signature metadata describe its
6222        // declaration/definition occurrences.
6223        self.add_declaration(code_unit.clone(), node, None, None);
6224        let signature = if recovered_constraint_constructor.is_some() {
6225            normalize_cpp_whitespace(node_text(function_declarator, self.source))
6226        } else {
6227            render_cpp_function_display_signature_from_node(
6228                node,
6229                self.source,
6230                scope.template_signature.as_deref(),
6231                true,
6232                ancestry,
6233            )
6234        };
6235        self.parsed.add_signature_with_metadata(
6236            code_unit.clone(),
6237            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
6238                .with_declaration_only(false)
6239                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
6240        );
6241        if let Some(parent) = &scope.class_unit {
6242            self.parsed.add_child(parent.clone(), code_unit);
6243        } else if let Some(module) = &scope.module {
6244            self.parsed.add_child(module.clone(), code_unit);
6245        }
6246        self.visit_c_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6247    }
6248
6249    /// Recover the namespace lost when tree-sitter promotes an export-macro
6250    /// class definition to a root-level `function_definition`.  Only a
6251    /// body-bearing, top-level recovery may borrow a namespace, and only when
6252    /// one earlier namespace-scope forward declaration proves the identity.
6253    fn scope_for_recovered_exported_class<'tree>(
6254        &mut self,
6255        node: Node<'tree>,
6256        name: &str,
6257        definition_body_present: bool,
6258        scope: &ScopeInfo,
6259        ancestry: &ParentIndex<'tree>,
6260    ) -> ScopeInfo {
6261        if !definition_body_present
6262            || !scope.package_name.is_empty()
6263            || scope.class_unit.is_some()
6264            || !(is_recovered_exported_class_container(node, self.source)
6265                || recover_function_like_export_class_pair(node, self.source).is_some()
6266                || recover_embedded_function_like_export_classes(node, self.source)
6267                    .iter()
6268                    .any(|recovered| recovered.name == name)
6269                || matches!(node.kind(), "declaration" | "field_declaration")
6270                    && recover_exported_class_declaration(node, self.source).is_some()
6271                || matches!(
6272                    node.kind(),
6273                    "class_specifier" | "struct_specifier" | "union_specifier"
6274                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6275                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6276                        name_node,
6277                        self.source,
6278                    )))
6279                }) || ancestry.parent(node).is_some_and(|parent| {
6280                    matches!(parent.kind(), "declaration" | "field_declaration")
6281                        && recover_exported_class_declaration(parent, self.source).is_some()
6282                        || is_recovered_exported_class_container(parent, self.source)
6283                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6284        {
6285            return scope.clone();
6286        }
6287        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6288        let Some(package_name) = borrowed_namespace
6289            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6290        else {
6291            return scope.clone();
6292        };
6293
6294        let module = CodeUnit::new_fq(
6295            self.file.clone(),
6296            CodeUnitType::Module,
6297            "",
6298            package_name.clone(),
6299            cpp_namespace_fq(&package_name),
6300        );
6301        let mut recovered = scope.clone();
6302        recovered.package_name = package_name;
6303        recovered.module = Some(module);
6304        recovered
6305    }
6306
6307    /// The unique namespace-scope forward declaration of `name` that precedes
6308    /// `recovered_node`, answered from the walk's carried-forward scan of the
6309    /// tree `recovered_node` belongs to.
6310    ///
6311    /// The scan is built on the first question and advanced by each later one,
6312    /// so a file that never reaches this path -- almost every file -- pays
6313    /// nothing, and one that reaches it thousands of times pays a single pass
6314    /// (#2754).
6315    fn unique_earlier_namespace_forward<'tree>(
6316        &mut self,
6317        recovered_node: Node<'tree>,
6318        name: &str,
6319        ancestry: &ParentIndex<'tree>,
6320    ) -> Option<String> {
6321        let mut root = recovered_node;
6322        while let Some(parent) = ancestry.parent(root) {
6323            root = parent;
6324        }
6325        let source = self.source;
6326        let scan = self
6327            .namespace_forward_scans
6328            .entry(CppTreeIdentity::of(root))
6329            .or_default();
6330        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6331        let borrowed = scan.unique_earlier_forward(name, recovered_node);
6332
6333        #[cfg(debug_assertions)]
6334        assert_eq!(
6335            borrowed,
6336            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6337            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6338             for {name} at byte {}",
6339            recovered_node.start_byte()
6340        );
6341
6342        borrowed
6343    }
6344
6345    fn visit_malformed_function_definition_container<'tree>(
6346        &mut self,
6347        node: Node<'tree>,
6348        scope: &ScopeInfo,
6349        stack: &mut Vec<CppWork<'tree>>,
6350    ) {
6351        let Some(body) = cpp_body_node(node) else {
6352            return;
6353        };
6354        if !cpp_contains_namespace_definition(body) {
6355            return;
6356        }
6357        stack.push(CppWork::Container(CppContainer {
6358            node: body,
6359            scope: scope.clone(),
6360        }));
6361    }
6362
6363    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
6364    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
6365    /// emits for a sentinel-prefixed region, reparse the interior after the
6366    /// sentinel identifier as real C++ items -- confined to the region so
6367    /// every reparsed node keeps its original byte/line position -- and run the
6368    /// ordinary container visitation over the result. Returns `true` when it fired
6369    /// (the caller must then skip normal function processing). Nested sentinel
6370    /// regions recover recursively: the reparsed interior is walked through the
6371    /// same `visit_function_definition` path, so a sentinel inside the region hits
6372    /// this recovery again.
6373    /// Runs `reparse_walk` and records every declaration it mints as a
6374    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
6375    /// `recovery` (issue #1657). A reparsed sentinel region has no single
6376    /// recovered envelope unit: the ordinary visitors mint namespaces,
6377    /// classes, and members directly from the reparsed tree, so the walk's
6378    /// declaration delta is the recovered set. Records are ordered by
6379    /// declaration start byte so the parse product stays deterministic.
6380    fn record_recovered_declarations(
6381        &mut self,
6382        recovery: Range,
6383        reparse_walk: impl FnOnce(&mut Self),
6384    ) {
6385        // The set difference this used to be, kept as the oracle every answer
6386        // is asserted against (#2787).
6387        #[cfg(any(debug_assertions, test))]
6388        let before = self.parsed.declarations().clone();
6389
6390        self.recovery_captures.push(CppRecoveryCapture::default());
6391        reparse_walk(self);
6392        let captured = self
6393            .recovery_captures
6394            .pop()
6395            .expect("the capture this call pushed is the one it pops");
6396
6397        // The capture holds every declaration created while it was open, once
6398        // each and in creation order, so the recovered set costs what the
6399        // recovery made rather than everything the file has declared so far.
6400        // One filter is left to apply: a created declaration that a later
6401        // deferred replacement removed is not in the parse product to report.
6402        let mut minted: Vec<CodeUnit> = captured
6403            .created
6404            .into_iter()
6405            .filter(|unit| self.parsed.contains_declaration(unit))
6406            .collect();
6407        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6408
6409        #[cfg(any(debug_assertions, test))]
6410        {
6411            let mut rediscovered: Vec<CodeUnit> = self
6412                .parsed
6413                .declarations()
6414                .iter()
6415                .filter(|unit| !before.contains(*unit))
6416                .cloned()
6417                .collect();
6418            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6419            assert_eq!(
6420                minted, rediscovered,
6421                "the captured recovered set must be the declaration delta of the reparse \
6422                 walk over {recovery:?}"
6423            );
6424        }
6425
6426        for unit in minted {
6427            self.parsed
6428                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6429                    recovery,
6430                    unit,
6431                });
6432        }
6433    }
6434
6435    /// Where one recovered declaration sorts: by start byte, then by name, so
6436    /// the parse product stays deterministic.
6437    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6438        let start = self
6439            .parsed
6440            .declaration_ranges(unit)
6441            .first()
6442            .map(|range| range.start_byte)
6443            .unwrap_or(usize::MAX);
6444        (start, unit.fq_name().to_string())
6445    }
6446
6447    fn visit_sentinel_macro_region<'tree>(
6448        &mut self,
6449        node: Node<'tree>,
6450        scope: &ScopeInfo,
6451        stack: &mut Vec<CppWork<'tree>>,
6452        ancestry: &ParentIndex<'tree>,
6453    ) -> bool {
6454        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6455            return true;
6456        }
6457        if let Some((
6458            reparse_start,
6459            class_start,
6460            body_start,
6461            class_close_start,
6462            class_close_end,
6463            class_close_line,
6464        )) = cpp_sentinel_macro_class_region(node, self.source)
6465        {
6466            let Some(class_tree) =
6467                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6468            else {
6469                return false;
6470            };
6471            let class_root = class_tree.root_node();
6472            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6473            // A region reparse is its own tree and needs its own parent index.
6474            let class_ancestry = ParentIndex::new(class_root);
6475            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6476                class_root,
6477                template_node,
6478                self.source,
6479                &class_ancestry,
6480            ) else {
6481                return false;
6482            };
6483            let class_node = reparsed_class.declaration_node;
6484            let name = reparsed_class.name;
6485            let mut class_scope = scope.clone();
6486            if let Some(template_node) = template_node {
6487                class_scope.template_signature =
6488                    cpp_template_signature(template_node, class_node, self.source);
6489                class_scope.template_metadata =
6490                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
6491            }
6492            let Some(body_tree) =
6493                cpp_reparse_region_items(self.source, body_start, class_close_start)
6494            else {
6495                return false;
6496            };
6497            let raw_supertypes = reparsed_class.raw_supertypes;
6498            let class_range = Range {
6499                start_byte: class_start,
6500                end_byte: class_close_end,
6501                start_line: class_node.start_position().row + 1,
6502                end_line: class_close_line,
6503            };
6504            let class_scope = self.scope_for_recovered_exported_class(
6505                class_node,
6506                &name,
6507                true,
6508                &class_scope,
6509                ancestry,
6510            );
6511            let mut class_stack = Vec::new();
6512            let class_unit = self.visit_named_class_like_shape(
6513                class_node,
6514                name,
6515                None,
6516                true,
6517                Some(class_range),
6518                raw_supertypes,
6519                &class_scope,
6520                &mut class_stack,
6521                ancestry,
6522            );
6523            self.parsed
6524                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6525                    recovery: class_range,
6526                    unit: class_unit.clone(),
6527                });
6528            let member_scope = ScopeInfo {
6529                package_name: class_scope.package_name.clone(),
6530                module: class_scope.module.clone(),
6531                class_unit: Some(class_unit),
6532                template_signature: class_scope.template_signature.clone(),
6533                template_metadata: None,
6534                declarations_are_fields: true,
6535                recovered_specialization_member_scope: false,
6536                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6537            };
6538            // The padded body reparse is its own tree, so it indexes itself.
6539            let body_root = body_tree.root_node();
6540            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6541            // Register only after the padded body reparse: its nodes deliberately
6542            // retain offsets inside the consumed region and must be visited first.
6543            self.consumed_fragment_regions
6544                .push((node.start_byte(), class_close_end));
6545            // An ERROR envelope can hold real sibling declarations after the
6546            // recovered class's close (the suffix-reparse boundary in
6547            // `cpp_sentinel_macro_class_region` partitions, it does not
6548            // consume). Walk the envelope's remaining children normally; the
6549            // consumed region above keeps the recovered class from being
6550            // indexed twice.
6551            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6552                stack.push(CppWork::Container(CppContainer {
6553                    node,
6554                    scope: scope.clone(),
6555                }));
6556            }
6557            return true;
6558        }
6559        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6560            return false;
6561        };
6562        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6563            return false;
6564        };
6565        let root = tree.root_node();
6566        if !cpp_reparsed_items_are_indexable(root, self.source) {
6567            return false;
6568        }
6569        let recovery = cpp_recovery_window(self.source, start, end);
6570        // The reparsed region is its own tree, so this walk indexes it itself.
6571        let reparsed_ancestry = ParentIndex::new(root);
6572        self.record_recovered_declarations(recovery, |visitor| {
6573            visitor.visit_container(
6574                root,
6575                &reparsed_ancestry,
6576                &scope.package_name,
6577                scope.module.clone(),
6578                scope.class_unit.clone(),
6579                scope.template_signature.clone(),
6580                scope.visible_using_namespaces.clone(),
6581            );
6582        });
6583        if end > node.end_byte() {
6584            self.consumed_fragment_regions
6585                .push((node.start_byte(), end));
6586        } else if node.kind() == "ERROR" && node.end_byte() > end {
6587            // The sentinel region ended at the first recovered class-like item
6588            // but the ERROR envelope keeps real sibling declarations after it
6589            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
6590            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
6591            // envelope's remaining children normally; the consumed region
6592            // keeps the reparsed prefix from being indexed twice.
6593            self.consumed_fragment_regions
6594                .push((node.start_byte(), end));
6595            stack.push(CppWork::Container(CppContainer {
6596                node,
6597                scope: scope.clone(),
6598            }));
6599        }
6600        true
6601    }
6602
6603    /// Re-own complete class declarations from the structured Abseil
6604    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
6605    /// its direct CST children already prove both namespace components and the
6606    /// class bodies, so the ordinary class/member visitor can retain ownership
6607    /// and exact source ranges without admitting unrelated callable bodies.
6608    fn visit_nested_namespace_sentinel<'tree>(
6609        &mut self,
6610        node: Node<'tree>,
6611        scope: &ScopeInfo,
6612        ancestry: &ParentIndex<'tree>,
6613    ) -> bool {
6614        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6615            return false;
6616        };
6617
6618        let mut package_name = scope.package_name.clone();
6619        let mut module = scope.module.clone();
6620        for component in recovered.namespace_components {
6621            package_name = if package_name.is_empty() {
6622                component
6623            } else {
6624                format!("{package_name}::{component}")
6625            };
6626            let namespace_module = CodeUnit::new_fq(
6627                self.file.clone(),
6628                CodeUnitType::Module,
6629                "",
6630                package_name.clone(),
6631                cpp_namespace_fq(&package_name),
6632            );
6633            if !self.parsed.contains_declaration(&namespace_module) {
6634                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6635            }
6636            module = Some(namespace_module);
6637        }
6638
6639        let recovered_scope = ScopeInfo {
6640            package_name,
6641            module,
6642            // This scope's package comes from the namespace levels the sentinel
6643            // shape proves, so its owner must come from the same place. C++
6644            // never nests a namespace inside a class, so a surviving
6645            // `class_unit` here is recovery bleed from an earlier malformed
6646            // region, and keeping it would publish declarations whose package
6647            // names one lexical position and whose owner names another -- the
6648            // hybrid identity that trips the package/short boundary assert in
6649            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
6650            // identity-neutral for valid code, where `class_unit` is always
6651            // empty at a namespace.
6652            class_unit: None,
6653            template_signature: scope.template_signature.clone(),
6654            template_metadata: scope.template_metadata.clone(),
6655            declarations_are_fields: false,
6656            recovered_specialization_member_scope: false,
6657            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6658        };
6659        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6660            recovered.function,
6661            recovered.body,
6662            self.source,
6663            ancestry,
6664        ) {
6665            let mut class_scope = recovered_scope.clone();
6666            if let Some(template_node) = fragmented.template_node {
6667                class_scope.template_signature =
6668                    cpp_template_signature(template_node, fragmented.class_node, self.source);
6669                class_scope.template_metadata = cpp_template_metadata(
6670                    template_node,
6671                    fragmented.class_node,
6672                    self.source,
6673                    ancestry,
6674                );
6675            }
6676            if let Some(outcome) = self
6677                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6678            {
6679                let mut class_stack = Vec::new();
6680                let class_unit = self.visit_named_class_like_shape(
6681                    fragmented.class_node,
6682                    fragmented.name.clone(),
6683                    None,
6684                    true,
6685                    Some(fragmented.fragmented.class_range),
6686                    fragmented.raw_supertypes.clone(),
6687                    &class_scope,
6688                    &mut class_stack,
6689                    ancestry,
6690                );
6691                self.parsed
6692                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6693                        recovery: fragmented.fragmented.class_range,
6694                        unit: class_unit.clone(),
6695                    });
6696                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6697                    self.consumed_fragment_regions.push((
6698                        fragmented.consumed_start,
6699                        fragmented.fragmented.class_range.end_byte,
6700                    ));
6701                }
6702            }
6703        }
6704        // The class requirement above is the admission gate; once admitted,
6705        // traverse the whole proven inner namespace body so sibling aliases,
6706        // functions, and variables are not silently discarded. The body is a
6707        // node of the tree being walked, so it reuses that tree's index.
6708        self.run_container_work(recovered.body, recovered_scope, ancestry);
6709        true
6710    }
6711
6712    fn visit_declaration<'tree>(
6713        &mut self,
6714        node: Node<'tree>,
6715        scope: &ScopeInfo,
6716        in_class_body: bool,
6717        stack: &mut Vec<CppWork<'tree>>,
6718        ancestry: &ParentIndex<'tree>,
6719    ) {
6720        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6721            return;
6722        }
6723        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6724            return;
6725        }
6726        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6727            !cpp_active_template_type_parameter(
6728                node,
6729                node_text(declarator, self.source),
6730                self.source,
6731                ancestry,
6732            )
6733        }) {
6734            return;
6735        }
6736        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6737            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
6738            // the following `Imaging image` member into one malformed field.
6739            // The ERROR's identifier is the actual declarator; route it
6740            // through the ordinary field path so its parent, range, and
6741            // signature metadata remain consistent with every other member.
6742            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6743            return;
6744        }
6745        if in_class_body
6746            && let Some(parent) = scope.class_unit.as_ref()
6747            && let Some(call) =
6748                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6749        {
6750            self.visit_recovered_macro_qualified_constructor_definition(
6751                node, call, scope, ancestry,
6752            );
6753            return;
6754        }
6755        if in_class_body
6756            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6757        {
6758            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6759            return;
6760        }
6761        if in_class_body
6762            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6763        {
6764            for member in members {
6765                self.add_macro_wrapped_declaration(member, scope, ancestry);
6766            }
6767            return;
6768        }
6769        if in_class_body
6770            && let Some(declarators) =
6771                recovered_macro_qualified_field_declarators(node, self.source)
6772        {
6773            for declarator in declarators {
6774                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6775            }
6776            return;
6777        }
6778        let recovered_alias_names = recovered_type_alias_names(node, self.source);
6779        if !recovered_alias_names.is_empty() {
6780            self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6781            return;
6782        }
6783        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
6784        {
6785            return;
6786        }
6787        if self.visit_c_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6788            return;
6789        }
6790
6791        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6792            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6793                // Issue #938: the members tree-sitter scattered out of the fragmented
6794                // multiple-base export node are reparsed from their true body region
6795                // and re-owned as members of the recovered class, with an explicit
6796                // navigation range spanning to the displaced closing brace.
6797                if let Some(outcome) =
6798                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6799                {
6800                    let consumed_region = (
6801                        recovered.declaration_node.end_byte(),
6802                        fragmented.class_range.end_byte,
6803                    );
6804                    let code_unit = self.visit_named_class_like_shape(
6805                        recovered.declaration_node,
6806                        recovered.name,
6807                        None,
6808                        true,
6809                        Some(fragmented.class_range),
6810                        recovered.raw_supertypes,
6811                        scope,
6812                        stack,
6813                        ancestry,
6814                    );
6815                    self.parsed.record_materialization(
6816                        MaterializationRecord::RecoveredDeclaration {
6817                            recovery: fragmented.class_range,
6818                            unit: code_unit.clone(),
6819                        },
6820                    );
6821                    let consume_fragment =
6822                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
6823                    // Everything between the fragmented declaration and its displaced
6824                    // closing brace now belongs to the recovered class; keep the
6825                    // ordinary walk from re-indexing those scattered siblings at top
6826                    // level. Register the consumed region only after indexing because
6827                    // the reparsed nodes retain byte offsets inside that same region.
6828                    if consume_fragment {
6829                        self.consumed_fragment_regions.push(consumed_region);
6830                    }
6831                    return;
6832                }
6833            }
6834            let uses_initializer_body = recovered.uses_initializer_body;
6835            let definition_body_present = recovered.body.is_some();
6836            let class_unit = self.visit_named_class_like_shape(
6837                recovered.declaration_node,
6838                recovered.name,
6839                recovered.body,
6840                definition_body_present,
6841                None,
6842                recovered.raw_supertypes,
6843                scope,
6844                stack,
6845                ancestry,
6846            );
6847            self.parsed
6848                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6849                    recovery: cpp_declaration_range(node),
6850                    unit: class_unit,
6851                });
6852            if uses_initializer_body {
6853                return;
6854            }
6855        }
6856
6857        let mut handled_function = false;
6858        let mut handled_declarator = false;
6859        let mut cursor = node.walk();
6860        for child in node.named_children(&mut cursor) {
6861            if matches!(
6862                child.kind(),
6863                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
6864            ) {
6865                // A named class-like definition remains a declaration even when
6866                // the same statement also declares an object, for example
6867                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
6868                // declaration's type and `kind` as its declarator.  Dropping the
6869                // type here loses both its nested owner and every later lexical
6870                // reference to it.  A body is the structured proof that this is
6871                // a definition rather than an elaborated type use such as
6872                // `class Kind value;`.
6873                if cpp_body_node(child).is_some() {
6874                    self.visit_class_like(child, scope, stack, ancestry);
6875                }
6876                continue;
6877            }
6878        }
6879
6880        let mut cursor = node.walk();
6881        for child in node.children_by_field_name("declarator", &mut cursor) {
6882            if crate::structural::is_recovered_designator_init_declarator(child) {
6883                handled_declarator = true;
6884                continue;
6885            }
6886            if in_class_body
6887                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
6888            {
6889                handled_declarator = true;
6890                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
6891                continue;
6892            }
6893            if let Some(kind) = classify_declarator(child) {
6894                handled_declarator = true;
6895                match kind {
6896                    DeclaratorKind::Function(function_declarator) => {
6897                        handled_function = true;
6898                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6899                    }
6900                    DeclaratorKind::Variable(variable_declarator) => {
6901                        self.visit_variable_declaration(
6902                            node,
6903                            variable_declarator,
6904                            scope,
6905                            in_class_body,
6906                            ancestry,
6907                        );
6908                    }
6909                }
6910            }
6911        }
6912
6913        if !handled_declarator {
6914            let mut cursor = node.walk();
6915            for child in node.named_children(&mut cursor) {
6916                if crate::structural::is_recovered_designator_init_declarator(child) {
6917                    handled_declarator = true;
6918                    continue;
6919                }
6920                if !is_unfielded_declarator_candidate(child) {
6921                    continue;
6922                }
6923                let Some(kind) = classify_declarator(child) else {
6924                    continue;
6925                };
6926                handled_declarator = true;
6927                match kind {
6928                    DeclaratorKind::Function(function_declarator) => {
6929                        handled_function = true;
6930                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
6931                    }
6932                    DeclaratorKind::Variable(variable_declarator) => {
6933                        self.visit_variable_declaration(
6934                            node,
6935                            variable_declarator,
6936                            scope,
6937                            in_class_body,
6938                            ancestry,
6939                        );
6940                    }
6941                }
6942            }
6943        }
6944
6945        if handled_function {
6946            return;
6947        }
6948
6949        if !handled_declarator {
6950            if in_class_body {
6951                self.visit_class_members_from_declaration(node, scope, ancestry);
6952            } else {
6953                self.visit_global_variables_from_declaration(node, scope, ancestry);
6954            }
6955        }
6956    }
6957
6958    /// Preserve the member structure of an anonymous C aggregate.
6959    ///
6960    /// An anonymous union with no declarator promotes its fields into the
6961    /// containing aggregate. An anonymous struct/union followed by a named
6962    /// declarator, such as `struct { T *ops; } sock`, declares both the field
6963    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
6964    /// the declarator's structured nested identity so a later `value.sock.ops`
6965    /// chain can traverse it without parsing a type spelling (#2407).
6966    fn visit_c_anonymous_aggregate_declaration<'tree>(
6967        &mut self,
6968        node: Node<'tree>,
6969        scope: &ScopeInfo,
6970        in_class_body: bool,
6971        stack: &mut Vec<CppWork<'tree>>,
6972        ancestry: &ParentIndex<'tree>,
6973    ) -> bool {
6974        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
6975            return false;
6976        }
6977        let Some(aggregate) = node.child_by_field_name("type") else {
6978            return false;
6979        };
6980        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
6981            || aggregate.child_by_field_name("name").is_some()
6982        {
6983            return false;
6984        }
6985        let Some(body) = cpp_body_node(aggregate) else {
6986            return false;
6987        };
6988
6989        let mut cursor = node.walk();
6990        let declarators = node
6991            .children_by_field_name("declarator", &mut cursor)
6992            .filter_map(|declarator| match classify_declarator(declarator) {
6993                Some(DeclaratorKind::Variable(variable)) => Some(variable),
6994                Some(DeclaratorKind::Function(_)) | None => None,
6995            })
6996            .collect::<Vec<_>>();
6997        if declarators.is_empty() {
6998            stack.push(CppWork::Container(CppContainer {
6999                node: body,
7000                scope: scope.clone(),
7001            }));
7002            return true;
7003        }
7004
7005        for declarator in declarators {
7006            let Some(name) = extract_variable_name(declarator, self.source) else {
7007                continue;
7008            };
7009            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7010            self.visit_named_class_like_shape(
7011                aggregate,
7012                name,
7013                Some(body),
7014                true,
7015                None,
7016                None,
7017                scope,
7018                stack,
7019                ancestry,
7020            );
7021        }
7022        true
7023    }
7024
7025    /// Give a function-local anonymous C aggregate a structured owner so its
7026    /// direct pointer bindings can be typed by the usage graph.  Unlike an
7027    /// anonymous aggregate in a class body, there is no source-level tag or
7028    /// typedef to supply an identity.  The aggregate's CST range is therefore
7029    /// part of a generated, collision-resistant identity; no source spelling
7030    /// is consulted.  The declaration's own variable remains a file-level
7031    /// field projection, while the aggregate body is visited as the generated
7032    /// class's field list.
7033    fn visit_c_anonymous_local_aggregate_declaration<'tree>(
7034        &mut self,
7035        node: Node<'tree>,
7036        scope: &ScopeInfo,
7037        stack: &mut Vec<CppWork<'tree>>,
7038        ancestry: &ParentIndex<'tree>,
7039    ) -> bool {
7040        if !self.c_tag_semantics || scope.class_unit.is_some() || !has_function_scope_ancestor(node)
7041        {
7042            return false;
7043        }
7044        let Some(aggregate) = node.child_by_field_name("type") else {
7045            return false;
7046        };
7047        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7048            || aggregate.child_by_field_name("name").is_some()
7049        {
7050            return false;
7051        }
7052        let Some(body) = cpp_body_node(aggregate) else {
7053            return false;
7054        };
7055        let mut cursor = node.walk();
7056        let declarators = node
7057            .children_by_field_name("declarator", &mut cursor)
7058            .filter_map(|declarator| match classify_declarator(declarator) {
7059                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7060                Some(DeclaratorKind::Function(_)) | None => None,
7061            })
7062            .collect::<Vec<_>>();
7063        if declarators.is_empty() {
7064            return false;
7065        }
7066
7067        for declarator in &declarators {
7068            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7069        }
7070        let name = format!("<anonymous:{}>", aggregate.start_byte());
7071        self.visit_named_class_like_shape(
7072            aggregate,
7073            name,
7074            Some(body),
7075            true,
7076            None,
7077            None,
7078            scope,
7079            stack,
7080            ancestry,
7081        );
7082        true
7083    }
7084
7085    fn visit_function_declaration<'tree>(
7086        &mut self,
7087        declaration_node: Node<'tree>,
7088        declarator: Node<'tree>,
7089        scope: &ScopeInfo,
7090        ancestry: &ParentIndex<'tree>,
7091    ) {
7092        let Some(function) = extract_function_info(declarator, self.source, scope) else {
7093            return;
7094        };
7095        let code_unit =
7096            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7097        if self.parsed.contains_declaration(&code_unit) {
7098            self.parsed
7099                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7100            return;
7101        }
7102        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7103        let signature = render_cpp_function_display_signature_from_node(
7104            declaration_node,
7105            self.source,
7106            scope.template_signature.as_deref(),
7107            false,
7108            ancestry,
7109        );
7110        self.parsed.add_signature_with_metadata(
7111            code_unit.clone(),
7112            cpp_signature_metadata(signature, declarator, self.source, ancestry)
7113                .with_declaration_only(true)
7114                .with_callable_linkage(cpp_callable_linkage(
7115                    declaration_node,
7116                    self.source,
7117                    ancestry,
7118                )),
7119        );
7120        if let Some(parent) = &scope.class_unit {
7121            self.parsed.add_child(parent.clone(), code_unit);
7122        } else if let Some(module) = &scope.module {
7123            self.parsed.add_child(module.clone(), code_unit);
7124        }
7125    }
7126
7127    fn visit_recovered_macro_qualified_function_declaration<'tree>(
7128        &mut self,
7129        declaration_node: Node<'tree>,
7130        call: Node<'tree>,
7131        scope: &ScopeInfo,
7132        ancestry: &ParentIndex<'tree>,
7133    ) {
7134        let Some(parent) = &scope.class_unit else {
7135            return;
7136        };
7137        let Some(name_node) = call.child_by_field_name("function") else {
7138            return;
7139        };
7140        let Some(arguments) = call.child_by_field_name("arguments") else {
7141            return;
7142        };
7143        let Some((signature, parameter_labels)) =
7144            recovered_macro_qualified_function_parameters(arguments, self.source)
7145        else {
7146            return;
7147        };
7148        let arity = parameter_labels.len();
7149        let function = FunctionInfo {
7150            package_name: scope.package_name.clone(),
7151            owner: Some(CppMemberOwner::Unit(parent.clone())),
7152            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7153            signature,
7154        };
7155        if function.name.is_empty() {
7156            return;
7157        }
7158        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7159        if self.parsed.contains_declaration(&code_unit) {
7160            self.parsed
7161                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7162            return;
7163        }
7164        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7165        let signature_label = render_cpp_function_display_signature_from_node(
7166            declaration_node,
7167            self.source,
7168            scope.template_signature.as_deref(),
7169            false,
7170            ancestry,
7171        );
7172        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7173            .with_declaration_only(true)
7174            .with_callable_arity(CallableArity::exact(arity))
7175            .with_callable_linkage(cpp_callable_linkage(
7176                declaration_node,
7177                self.source,
7178                ancestry,
7179            ));
7180        self.parsed
7181            .add_signature_with_metadata(code_unit.clone(), metadata);
7182        self.parsed.add_child(parent.clone(), code_unit);
7183    }
7184
7185    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7186        &mut self,
7187        declaration_node: Node<'tree>,
7188        call: Node<'tree>,
7189        scope: &ScopeInfo,
7190        ancestry: &ParentIndex<'tree>,
7191    ) {
7192        let Some(parent) = &scope.class_unit else {
7193            return;
7194        };
7195        let Some(arguments) = call.child_by_field_name("arguments") else {
7196            return;
7197        };
7198        let Some((mut signature, parameter_labels)) =
7199            recovered_macro_qualified_function_parameters(arguments, self.source)
7200        else {
7201            return;
7202        };
7203        if let Some(template_signature) = &scope.template_signature {
7204            signature = format!("{template_signature}{signature}");
7205        }
7206        let arity = parameter_labels.len();
7207        let function = FunctionInfo {
7208            package_name: scope.package_name.clone(),
7209            owner: Some(CppMemberOwner::Unit(parent.clone())),
7210            name: parent.identifier().to_string(),
7211            signature,
7212        };
7213        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7214        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7215        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7216        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7217            .with_declaration_only(false)
7218            .with_callable_arity(CallableArity::exact(arity))
7219            .with_callable_linkage(cpp_callable_linkage(
7220                declaration_node,
7221                self.source,
7222                ancestry,
7223            ));
7224        self.parsed
7225            .add_signature_with_metadata(code_unit.clone(), metadata);
7226        self.parsed.add_child(parent.clone(), code_unit);
7227    }
7228
7229    fn visit_variable_declaration<'tree>(
7230        &mut self,
7231        declaration_node: Node<'tree>,
7232        declarator: Node<'tree>,
7233        scope: &ScopeInfo,
7234        in_class_body: bool,
7235        ancestry: &ParentIndex<'tree>,
7236    ) {
7237        let Some(name) = extract_variable_name(declarator, self.source) else {
7238            return;
7239        };
7240        let parent = if in_class_body {
7241            let Some(parent) = &scope.class_unit else {
7242                return;
7243            };
7244            Some(parent)
7245        } else {
7246            None
7247        };
7248        let short_name = match parent {
7249            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7250            None => name.clone(),
7251        };
7252        let fq = cpp_leaf_fq(
7253            &scope.package_name,
7254            parent,
7255            &name,
7256            SegmentKind::Member,
7257            SegmentKind::Member,
7258        );
7259        let code_unit = CodeUnit::new_fq(
7260            self.file.clone(),
7261            CodeUnitType::Field,
7262            scope.package_name.clone(),
7263            short_name,
7264            fq,
7265        );
7266        if self.parsed.contains_declaration(&code_unit) {
7267            return;
7268        }
7269        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7270        self.parsed.add_signature_with_metadata(
7271            code_unit.clone(),
7272            SignatureMetadata::new(
7273                render_cpp_field_signature(declaration_node, declarator, self.source),
7274                Vec::new(),
7275            )
7276            .with_cpp_field_linkage(cpp_field_declaration_linkage(
7277                declaration_node,
7278                self.source,
7279                ancestry,
7280            )),
7281        );
7282        if let Some(parent) = &scope.class_unit {
7283            self.parsed.add_child(parent.clone(), code_unit);
7284        } else if let Some(module) = &scope.module {
7285            self.parsed.add_child(module.clone(), code_unit);
7286        }
7287    }
7288
7289    fn visit_class_members_from_declaration<'tree>(
7290        &mut self,
7291        node: Node<'tree>,
7292        scope: &ScopeInfo,
7293        ancestry: &ParentIndex<'tree>,
7294    ) {
7295        let mut cursor = node.walk();
7296        for child in node.named_children(&mut cursor) {
7297            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7298                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7299            } else if child.kind() == "init_declarator"
7300                && let Some(inner) = child.child_by_field_name("declarator")
7301            {
7302                self.visit_variable_declaration(node, inner, scope, true, ancestry);
7303            } else if matches!(
7304                child.kind(),
7305                "identifier"
7306                    | "field_identifier"
7307                    | "pointer_declarator"
7308                    | "reference_declarator"
7309                    | "array_declarator"
7310                    | "parenthesized_declarator"
7311            ) {
7312                self.visit_variable_declaration(node, child, scope, true, ancestry);
7313            }
7314        }
7315    }
7316
7317    fn visit_global_variables_from_declaration<'tree>(
7318        &mut self,
7319        node: Node<'tree>,
7320        scope: &ScopeInfo,
7321        ancestry: &ParentIndex<'tree>,
7322    ) {
7323        let mut cursor = node.walk();
7324        for child in node.named_children(&mut cursor) {
7325            if child.kind() == "init_declarator"
7326                && let Some(inner) = child.child_by_field_name("declarator")
7327            {
7328                self.visit_variable_declaration(node, inner, scope, false, ancestry);
7329            } else if matches!(
7330                child.kind(),
7331                "identifier"
7332                    | "field_identifier"
7333                    | "pointer_declarator"
7334                    | "reference_declarator"
7335                    | "array_declarator"
7336                    | "parenthesized_declarator"
7337            ) {
7338                self.visit_variable_declaration(node, child, scope, false, ancestry);
7339            }
7340        }
7341    }
7342
7343    fn visit_type_declaration<'tree>(
7344        &mut self,
7345        node: Node<'tree>,
7346        scope: &ScopeInfo,
7347        stack: &mut Vec<CppWork<'tree>>,
7348        ancestry: &ParentIndex<'tree>,
7349    ) {
7350        let type_node = node.child_by_field_name("type");
7351        if let Some(type_node) = type_node
7352            && matches!(
7353                type_node.kind(),
7354                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7355            )
7356        {
7357            self.visit_class_like(type_node, scope, stack, ancestry);
7358        }
7359
7360        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7361            let range = Range {
7362                start_byte: node.start_byte(),
7363                end_byte: recovered.end_node.end_byte(),
7364                start_line: node.start_position().row + 1,
7365                end_line: recovered.end_node.end_position().row + 1,
7366            };
7367            let signature = self
7368                .source
7369                .get(range.start_byte..range.end_byte)
7370                .map(normalize_cpp_whitespace)
7371                .unwrap_or_default();
7372            self.record_type_aliases(
7373                node,
7374                scope,
7375                vec![recovered.name],
7376                signature,
7377                range,
7378                ancestry,
7379            );
7380            return;
7381        }
7382
7383        let alias_names = match node.kind() {
7384            "alias_declaration" => extract_alias_declaration_name(node, self.source)
7385                .into_iter()
7386                .collect::<Vec<_>>(),
7387            "type_definition" => extract_typedef_alias_names(node, self.source),
7388            _ => Vec::new(),
7389        };
7390        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7391            (type_node, alias_names.as_slice())
7392            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7393            && type_node.child_by_field_name("name").is_none()
7394        {
7395            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7396        } else {
7397            None
7398        };
7399        self.add_type_aliases(node, scope, alias_names, ancestry);
7400        if let Some((body, alias_name)) = anonymous_aggregate {
7401            // The typedef alias is also the only user-visible identity of an
7402            // anonymous aggregate. Reuse it as the member owner instead of
7403            // minting a second signatureless class with the same FQN. The
7404            // latter makes forward lookup ambiguous when conditional aliases
7405            // coexist and returns duplicate definitions even without guards.
7406            let signature = normalize_cpp_whitespace(node_text(node, self.source));
7407            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7408            debug_assert!(self.parsed.contains_declaration(&alias_unit));
7409            let mut nested_scope = scope.clone();
7410            nested_scope.class_unit = Some(alias_unit);
7411            nested_scope.template_signature = scope.template_signature.clone();
7412            nested_scope.template_metadata = None;
7413            nested_scope.declarations_are_fields = false;
7414            nested_scope.recovered_specialization_member_scope = false;
7415            stack.push(CppWork::Container(CppContainer {
7416                node: body,
7417                scope: nested_scope,
7418            }));
7419        }
7420    }
7421
7422    fn add_type_aliases(
7423        &mut self,
7424        node: Node<'_>,
7425        scope: &ScopeInfo,
7426        alias_names: Vec<String>,
7427        ancestry: &ParentIndex<'_>,
7428    ) {
7429        let signature = normalize_cpp_whitespace(node_text(node, self.source));
7430        self.record_type_aliases(
7431            node,
7432            scope,
7433            alias_names,
7434            signature,
7435            cpp_declaration_range(node),
7436            ancestry,
7437        );
7438    }
7439
7440    fn record_type_aliases(
7441        &mut self,
7442        node: Node<'_>,
7443        scope: &ScopeInfo,
7444        alias_names: Vec<String>,
7445        signature: String,
7446        range: Range,
7447        ancestry: &ParentIndex<'_>,
7448    ) {
7449        if signature.is_empty() {
7450            return;
7451        }
7452        let type_name = node
7453            .child_by_field_name("type")
7454            .and_then(|type_node| type_node.child_by_field_name("name"))
7455            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7456        for alias_name in alias_names {
7457            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7458                continue;
7459            }
7460            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7461            // Declaration identity does not include the alias signature. Keep
7462            // each physical range so conditional aliases retain their guards.
7463            self.add_declaration_with_range(code_unit.clone(), range, None, None);
7464            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7465            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7466                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7467            });
7468            self.parsed.add_signature_with_metadata(
7469                code_unit.clone(),
7470                SignatureMetadata::new(signature.clone(), Vec::new())
7471                    .with_underlying_type_identity(underlying_type_identity),
7472            );
7473            if let Some(metadata) = &scope.template_metadata {
7474                let mut metadata = metadata.clone();
7475                metadata.primary_fq_name = code_unit.fq_name();
7476                self.parsed
7477                    .set_cpp_template_metadata(code_unit.clone(), metadata);
7478            }
7479            if let Some(parent) = &scope.class_unit {
7480                self.parsed.add_child(parent.clone(), code_unit.clone());
7481            } else if let Some(module) = &scope.module {
7482                self.parsed.add_child(module.clone(), code_unit.clone());
7483            }
7484            self.parsed.mark_type_alias(code_unit);
7485        }
7486    }
7487
7488    fn type_alias_unit(
7489        &self,
7490        scope: &ScopeInfo,
7491        alias_name: String,
7492        signature: String,
7493    ) -> CodeUnit {
7494        let short_name = if let Some(parent) = &scope.class_unit {
7495            cpp_join_nested_short(parent.short_name(), &alias_name)
7496        } else {
7497            alias_name.clone()
7498        };
7499        let fq = cpp_leaf_fq(
7500            &scope.package_name,
7501            scope.class_unit.as_ref(),
7502            &alias_name,
7503            SegmentKind::Nested,
7504            SegmentKind::Type,
7505        );
7506        CodeUnit::with_signature_and_fq(
7507            self.file.clone(),
7508            CodeUnitType::Class,
7509            scope.package_name.clone(),
7510            short_name,
7511            Some(signature),
7512            false,
7513            fq,
7514        )
7515    }
7516
7517    fn visit_macro(&mut self, node: Node<'_>) {
7518        // A comment can terminate tree-sitter's preproc_arg before the logical
7519        // directive ends. Its remaining declarations are replacement locals,
7520        // not file-scope fields, even when recovery exposes them as siblings.
7521        if let Some(replacement) =
7522            crate::graph::syntax::function_macro_replacement_span(node, self.source)
7523        {
7524            self.consumed_fragment_regions
7525                .push((replacement.start, replacement.end));
7526        }
7527        let Some(name) = extract_macro_name(node, self.source) else {
7528            return;
7529        };
7530        let signature = node_text(node, self.source).trim_end().to_string();
7531        if signature.is_empty() {
7532            return;
7533        }
7534        let fq = cpp_member_fq("", &name);
7535        // A macro can be undefined and redefined later in the same file. Its
7536        // structured directive is part of the declaration identity so the
7537        // temporal environment can navigate to the definition active at a
7538        // reference instead of collapsing every spelling to the first range.
7539        // The same physical directive parsed through another C/C++ reading
7540        // still produces the same unit and remains deduplicated.
7541        let code_unit = CodeUnit::with_signature_and_fq(
7542            self.file.clone(),
7543            CodeUnitType::Macro,
7544            "",
7545            name.clone(),
7546            Some(signature.clone()),
7547            false,
7548            fq,
7549        );
7550        if !self.parsed.contains_declaration(&code_unit) {
7551            self.add_declaration(code_unit.clone(), node, None, None);
7552            let name_range = node
7553                .child_by_field_name("name")
7554                .map(cpp_declaration_range)
7555                .unwrap_or_else(|| cpp_declaration_range(node));
7556            self.parsed
7557                .record_materialization(MaterializationRecord::GeneratedDeclaration {
7558                    site: cpp_declaration_range(node),
7559                    argument: name_range,
7560                    kind: GenerationKind::PreprocessorDefinition,
7561                    unit: code_unit.clone(),
7562                });
7563            self.parsed.add_signature(code_unit, signature);
7564        }
7565        if node.kind() == "preproc_def" {
7566            update_object_macro_field_environment(
7567                node,
7568                self.source,
7569                &mut self.object_macro_fields,
7570                &mut self.ambiguous_object_macro_fields,
7571            );
7572        } else {
7573            self.object_macro_fields.remove(&name);
7574            self.ambiguous_object_macro_fields.remove(&name);
7575        }
7576    }
7577
7578    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7579        let Some(directive) = node.child_by_field_name("directive") else {
7580            return;
7581        };
7582        let name = node_text(directive, self.source).trim();
7583        let range = cpp_declaration_range(node);
7584        let fields = object_macro_field_closure(&self.object_macro_fields, name);
7585        self.materialize_object_macro_fields(fields, range, scope);
7586    }
7587
7588    /// Bare object-like field-list macros inside an otherwise well-formed
7589    /// aggregate remain identifier nodes in a field declaration. More than
7590    /// one adjacent invocation can be folded into the same declaration, so
7591    /// inspect all of its structured identifier nodes.
7592    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7593        if !matches!(node.kind(), "declaration" | "field_declaration") {
7594            return false;
7595        }
7596        // Claiming the declaration means its members are accounted for, which
7597        // is only true when there is an owner to materialize them into. A file
7598        // scope reaches this with a declaration that merely *contains* an
7599        // aggregate spelling an invocation (tree-sitter wraps libuv's
7600        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
7601        // `field_declaration` under a malformed header), and consuming it there
7602        // would discard the aggregate itself (#2985).
7603        if scope.class_unit.is_none() {
7604            return false;
7605        }
7606        let macro_nodes =
7607            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7608        for macro_node in &macro_nodes {
7609            let name = node_text(*macro_node, self.source).trim();
7610            let fields = object_macro_field_closure(&self.object_macro_fields, name);
7611            self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7612        }
7613        let Some(last) = macro_nodes.last() else {
7614            return false;
7615        };
7616        // Claiming the declaration accounts for every member it spells, not
7617        // only the donated ones: an invocation folded together with the
7618        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
7619        // leaves those members shapeless, and the grammar recovers them from
7620        // the byte range that follows the invocation.
7621        self.record_collapsed_aggregate_fields(
7622            last.end_byte()..node.end_byte(),
7623            node.start_position().row
7624                + 1
7625                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7626            scope,
7627        );
7628        true
7629    }
7630
7631    fn materialize_object_macro_fields(
7632        &mut self,
7633        fields: Vec<MacroReplacementField>,
7634        range: Range,
7635        scope: &ScopeInfo,
7636    ) {
7637        let Some(owner) = scope.class_unit.as_ref() else {
7638            return;
7639        };
7640        for field in fields {
7641            let signature = field.declaration.clone();
7642            let mut fq = owner.fq().clone();
7643            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7644            let short_name = if owner.short_name().is_empty() {
7645                field.name.clone()
7646            } else {
7647                format!("{}.{}", owner.short_name(), field.name)
7648            };
7649            let code_unit = CodeUnit::with_signature_and_fq(
7650                self.file.clone(),
7651                CodeUnitType::Field,
7652                owner.package_name().to_string(),
7653                short_name,
7654                Some(field.declaration),
7655                true,
7656                fq,
7657            );
7658            if self.parsed.contains_declaration(&code_unit) {
7659                continue;
7660            }
7661            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7662            self.parsed.add_signature(code_unit, signature);
7663        }
7664    }
7665
7666    /// Recover ordinary aggregates whose field-list macro invocations made
7667    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
7668    /// The class markers, aggregate braces, field nodes, and macro identifier
7669    /// nodes are all read from the AST; no source delimiter or token scan is
7670    /// used. A close-brace node inside a malformed field closes the innermost
7671    /// recovered aggregate, preserving nested-owner identity.
7672    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7673        let mut cursor = node.walk();
7674        let children = node.children(&mut cursor).collect::<Vec<_>>();
7675        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7676        let mut object_macro_fields = self.object_macro_fields.clone();
7677        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7678        let mut open = Vec::<usize>::new();
7679        let mut index = 0;
7680        while index < children.len() {
7681            let keyword = children[index];
7682            if update_object_macro_field_environment(
7683                keyword,
7684                self.source,
7685                &mut object_macro_fields,
7686                &mut ambiguous_object_macro_fields,
7687            ) {
7688                index += 1;
7689                continue;
7690            }
7691            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7692                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7693                if !name.is_empty() {
7694                    let parent = open
7695                        .last()
7696                        .and_then(|class| recovered.get(*class))
7697                        .map(|(owner, _, _, _)| owner.clone())
7698                        .or_else(|| scope.class_unit.clone());
7699                    let short_name = parent.as_ref().map_or_else(
7700                        || name.clone(),
7701                        |parent| cpp_join_nested_short(parent.short_name(), &name),
7702                    );
7703                    let fq = cpp_leaf_fq(
7704                        &scope.package_name,
7705                        parent.as_ref(),
7706                        &name,
7707                        SegmentKind::Nested,
7708                        SegmentKind::Type,
7709                    );
7710                    let owner = CodeUnit::with_signature_and_fq(
7711                        self.file.clone(),
7712                        CodeUnitType::Class,
7713                        scope.package_name.clone(),
7714                        short_name,
7715                        None,
7716                        false,
7717                        fq,
7718                    );
7719                    recovered.push((
7720                        owner,
7721                        head.key.start_byte(),
7722                        head.opening.end_byte(),
7723                        Vec::new(),
7724                    ));
7725                    let class_index = recovered.len() - 1;
7726                    match head.folded_members {
7727                        // The ordinary shape leaves the aggregate open: every
7728                        // child that follows belongs to it until a close brace
7729                        // ends it.
7730                        None => open.push(class_index),
7731                        // A folded head carries its own member list and its own
7732                        // closing brace, so it closes here. The invocations that
7733                        // precede the class key are the dangling tail of the
7734                        // replacement that folded it, and belong to whatever
7735                        // aggregate is open around it, not to this one.
7736                        Some(members) => {
7737                            let macro_nodes = object_macro_identifier_nodes_with_environment(
7738                                children[index],
7739                                self.source,
7740                                &mut object_macro_fields,
7741                                &mut ambiguous_object_macro_fields,
7742                            );
7743                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7744                                .iter()
7745                                .partition(|node| node.start_byte() < head.key.start_byte());
7746                            if let Some(&enclosing) = open.last() {
7747                                for macro_node in preceding {
7748                                    recovered[enclosing]
7749                                        .3
7750                                        .push(CppCollapsedMember::MacroFields {
7751                                            range: cpp_declaration_range(macro_node),
7752                                            fields: object_macro_field_closure(
7753                                                &object_macro_fields,
7754                                                &normalize_cpp_whitespace(node_text(
7755                                                    macro_node,
7756                                                    self.source,
7757                                                )),
7758                                            ),
7759                                        });
7760                                }
7761                            }
7762                            let closing = cpp_collapsed_aggregate_closing_brace(members);
7763                            recovered[class_index]
7764                                .3
7765                                .extend(cpp_collapsed_aggregate_members(
7766                                    &inner,
7767                                    head.opening.end_byte()..closing,
7768                                    head.opening.end_position().row + 1,
7769                                    self.source,
7770                                    &object_macro_fields,
7771                                ));
7772                            recovered[class_index].2 = members.end_byte();
7773                            for &open_class in &open {
7774                                recovered[open_class].2 =
7775                                    recovered[open_class].2.max(members.end_byte());
7776                            }
7777                        }
7778                    }
7779                    index += head.width;
7780                    continue;
7781                }
7782            }
7783            if let Some(&class_index) = open.last()
7784                && children[index].kind() == "field_declaration"
7785            {
7786                let field = children[index];
7787                let macro_nodes = object_macro_identifier_nodes_with_environment(
7788                    field,
7789                    self.source,
7790                    &mut object_macro_fields,
7791                    &mut ambiguous_object_macro_fields,
7792                );
7793                recovered[class_index]
7794                    .3
7795                    .extend(cpp_collapsed_aggregate_members(
7796                        &macro_nodes,
7797                        field.start_byte()..field.end_byte(),
7798                        field.start_position().row + 1,
7799                        self.source,
7800                        &object_macro_fields,
7801                    ));
7802                let end = field.end_byte();
7803                for &open_class in &open {
7804                    recovered[open_class].2 = recovered[open_class].2.max(end);
7805                }
7806                let closes = count_close_brace_nodes(field);
7807                for _ in 0..closes {
7808                    if let Some(closed) = open.pop() {
7809                        recovered[closed].2 = end;
7810                    }
7811                }
7812            }
7813            index += 1;
7814        }
7815
7816        let mut owners = Vec::with_capacity(recovered.len());
7817        for (owner, start, end, members) in recovered {
7818            let parent = owners
7819                .iter()
7820                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
7821                .cloned()
7822                .or_else(|| scope.class_unit.clone());
7823            self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
7824            owners.push(owner);
7825        }
7826    }
7827
7828    /// Record one recovered aggregate and the members it declares.
7829    fn declare_collapsed_aggregate(
7830        &mut self,
7831        owner: CodeUnit,
7832        span: std::ops::Range<usize>,
7833        parent: Option<CodeUnit>,
7834        members: Vec<CppCollapsedMember>,
7835        scope: &ScopeInfo,
7836    ) {
7837        let range = Range {
7838            start_byte: span.start,
7839            end_byte: span.end,
7840            start_line: self.source.get(..span.start).map_or(1, |source| {
7841                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7842            }),
7843            end_line: self.source.get(..span.end).map_or(1, |source| {
7844                source.bytes().filter(|byte| *byte == b'\n').count() + 1
7845            }),
7846        };
7847        // The tag can already be declared by a typedef or a forward
7848        // declaration, and this range is the definition's. Record it either
7849        // way: a declaration carries every range it is written at, and
7850        // dropping this one leaves nothing able to prove that the aggregate
7851        // has a body (#3098). Recording a range that is already held, or a
7852        // unit that is already declared, changes nothing.
7853        self.add_declaration_with_range(owner.clone(), range, parent, None);
7854        let owner_scope = ScopeInfo {
7855            class_unit: Some(owner),
7856            declarations_are_fields: true,
7857            ..scope.clone()
7858        };
7859        for member in members {
7860            match member {
7861                CppCollapsedMember::MacroFields { range, fields } => {
7862                    self.materialize_object_macro_fields(fields, range, &owner_scope);
7863                }
7864                CppCollapsedMember::Declarations { span, start_line } => {
7865                    self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
7866                }
7867            }
7868        }
7869    }
7870
7871    /// Recover an aggregate whose head tree-sitter folded into the declaration
7872    /// that precedes it, outside any collapsed `ERROR` container.
7873    ///
7874    /// A comment inside a field-list macro's replacement ends the replacement
7875    /// token, so the rest of the replacement is read at file scope. When the
7876    /// aggregate that follows declares its member list with one invocation,
7877    /// the parser reads the dangling tail as that declaration's type, the
7878    /// class key as an `ERROR`, and the member list as an initializer (#3098).
7879    /// The invocations before the class key belong to the replacement, not to
7880    /// this aggregate, so they go to whatever aggregate encloses it.
7881    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7882        if node.parent().is_some_and(|parent| parent.is_error()) {
7883            // The container's own recovery reads this child in source order,
7884            // with the preprocessor environment its earlier children build.
7885            return false;
7886        }
7887        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
7888            return false;
7889        };
7890        let members = head
7891            .folded_members
7892            .expect("a folded aggregate head carries its member list");
7893        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7894        if name.is_empty() {
7895            return false;
7896        }
7897        let macro_nodes =
7898            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7899        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7900            .iter()
7901            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
7902        for macro_node in preceding {
7903            let fields = object_macro_field_closure(
7904                &self.object_macro_fields,
7905                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
7906            );
7907            self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
7908        }
7909        let parent = scope.class_unit.clone();
7910        let short_name = parent.as_ref().map_or_else(
7911            || name.clone(),
7912            |parent| cpp_join_nested_short(parent.short_name(), &name),
7913        );
7914        let fq = cpp_leaf_fq(
7915            &scope.package_name,
7916            parent.as_ref(),
7917            &name,
7918            SegmentKind::Nested,
7919            SegmentKind::Type,
7920        );
7921        let owner = CodeUnit::with_signature_and_fq(
7922            self.file.clone(),
7923            CodeUnitType::Class,
7924            scope.package_name.clone(),
7925            short_name,
7926            None,
7927            false,
7928            fq,
7929        );
7930        let recovered = cpp_collapsed_aggregate_members(
7931            &inner,
7932            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
7933            head.opening.end_position().row + 1,
7934            self.source,
7935            &self.object_macro_fields,
7936        );
7937        self.declare_collapsed_aggregate(
7938            owner,
7939            head.key.start_byte()..members.end_byte(),
7940            parent,
7941            recovered,
7942            scope,
7943        );
7944        true
7945    }
7946
7947    /// Record the members of a collapsed aggregate region as fields of its
7948    /// recovered owner. The names, declaration text, and byte ranges all come
7949    /// from the grammar's reading of the region, so a region that is not a
7950    /// member list contributes nothing.
7951    fn record_collapsed_aggregate_fields(
7952        &mut self,
7953        span: std::ops::Range<usize>,
7954        start_line: usize,
7955        scope: &ScopeInfo,
7956    ) {
7957        let Some(owner) = scope.class_unit.as_ref() else {
7958            return;
7959        };
7960        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
7961            let range = Range {
7962                start_byte: field.range.start,
7963                end_byte: field.range.end,
7964                start_line: start_line
7965                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
7966                end_line: start_line
7967                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
7968            };
7969            let mut fq = owner.fq().clone();
7970            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7971            let short_name = if owner.short_name().is_empty() {
7972                field.name.clone()
7973            } else {
7974                format!("{}.{}", owner.short_name(), field.name)
7975            };
7976            let signature = normalize_cpp_whitespace(&field.declaration);
7977            let code_unit = CodeUnit::with_signature_and_fq(
7978                self.file.clone(),
7979                CodeUnitType::Field,
7980                owner.package_name().to_string(),
7981                short_name,
7982                Some(signature.clone()),
7983                false,
7984                fq,
7985            );
7986            if self.parsed.contains_declaration(&code_unit) {
7987                continue;
7988            }
7989            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7990            self.parsed.add_signature(code_unit, signature);
7991        }
7992    }
7993
7994    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7995        let Some(_directive) = node.child_by_field_name("directive") else {
7996            return;
7997        };
7998        if is_cpp_undef_directive(node, self.source) {
7999            update_object_macro_field_environment(
8000                node,
8001                self.source,
8002                &mut self.object_macro_fields,
8003                &mut self.ambiguous_object_macro_fields,
8004            );
8005            return;
8006        }
8007        let directly_in_field_list = node
8008            .parent()
8009            .is_some_and(|parent| parent.kind() == "field_declaration_list");
8010        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8011            self.visit_object_macro_fields(node, scope);
8012        }
8013    }
8014}
8015
8016/// Every member a field-list macro contributes under `environment`, including
8017/// the members of the field-list macros it composes.
8018///
8019/// Composition is resolved at the invocation, not at the definition, because a
8020/// nested name's active replacement is a property of the environment the
8021/// invocation sees. The traversal is iterative and visits each name once, so a
8022/// self-composing or mutually composing pair terminates.
8023fn object_macro_field_closure(
8024    environment: &HashMap<String, ObjectMacroReplacement>,
8025    name: &str,
8026) -> Vec<MacroReplacementField> {
8027    let mut fields = Vec::new();
8028    let mut visited = HashSet::default();
8029    let mut stack = vec![name.to_string()];
8030    while let Some(current) = stack.pop() {
8031        if !visited.insert(current.clone()) {
8032            continue;
8033        }
8034        let Some(replacement) = environment.get(&current) else {
8035            continue;
8036        };
8037        fields.extend(replacement.fields.iter().cloned());
8038        stack.extend(replacement.nested.iter().rev().cloned());
8039    }
8040    fields
8041}
8042
8043/// The structured replacement an object-like `#define` contributes.
8044///
8045/// The directive's whole logical line is the replacement list, which
8046/// [`object_macro_replacement_span`] recovers, because a comment inside the
8047/// replacement ends tree-sitter's `preproc_arg` token early.
8048fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8049    crate::graph::syntax::object_macro_replacement_span(node, source)
8050        .and_then(|span| source.get(span))
8051        .map(crate::graph::syntax::object_macro_replacement)
8052        .unwrap_or_default()
8053}
8054
8055/// Apply one preprocessor directive to an object-like field-list environment.
8056/// The environment is intentionally separate from the declaration visitor so
8057/// malformed parser regions can replay directives in source order without
8058/// mutating the live walk's state ahead of those directives.
8059fn update_object_macro_field_environment(
8060    node: Node<'_>,
8061    source: &str,
8062    fields: &mut HashMap<String, ObjectMacroReplacement>,
8063    ambiguous: &mut HashSet<String>,
8064) -> bool {
8065    match node.kind() {
8066        "preproc_def" => {
8067            let Some(name) = extract_macro_name(node, source) else {
8068                return false;
8069            };
8070            let replacement = object_macro_replacement_of(node, source);
8071            if replacement.is_empty() || ambiguous.contains(&name) {
8072                fields.remove(&name);
8073                ambiguous.insert(name);
8074            } else if let Some(previous) = fields.get(&name) {
8075                if previous != &replacement {
8076                    fields.remove(&name);
8077                    ambiguous.insert(name);
8078                }
8079            } else {
8080                fields.insert(name, replacement);
8081            }
8082            true
8083        }
8084        "preproc_call" if is_cpp_undef_directive(node, source) => {
8085            if let Some(argument) = node.child_by_field_name("argument") {
8086                let name = node_text(argument, source).trim();
8087                fields.remove(name);
8088                if inside_preprocessor_conditional(node) {
8089                    ambiguous.insert(name.to_string());
8090                } else {
8091                    ambiguous.remove(name);
8092                }
8093            }
8094            true
8095        }
8096        _ => false,
8097    }
8098}
8099
8100fn object_macro_identifier_nodes<'tree>(
8101    node: Node<'tree>,
8102    source: &str,
8103    fields: &HashMap<String, ObjectMacroReplacement>,
8104) -> Vec<Node<'tree>> {
8105    let mut result = Vec::new();
8106    let mut stack = vec![node];
8107    while let Some(current) = stack.pop() {
8108        if matches!(
8109            current.kind(),
8110            "identifier" | "field_identifier" | "type_identifier"
8111        ) && fields.contains_key(node_text(current, source).trim())
8112        {
8113            result.push(current);
8114        }
8115        let mut cursor = current.walk();
8116        let children = current.children(&mut cursor).collect::<Vec<_>>();
8117        stack.extend(children.into_iter().rev());
8118    }
8119    result.sort_by_key(|node| node.start_byte());
8120    result
8121}
8122
8123/// Collect macro identifiers while replaying any nested preprocessor
8124/// directives in source order. Recovery regions can contain a later `#undef`
8125/// in the same parser error envelope; using the live visitor map for the
8126/// entire envelope would incorrectly materialize invocations after it.
8127fn object_macro_identifier_nodes_with_environment<'tree>(
8128    node: Node<'tree>,
8129    source: &str,
8130    fields: &mut HashMap<String, ObjectMacroReplacement>,
8131    ambiguous: &mut HashSet<String>,
8132) -> Vec<Node<'tree>> {
8133    let mut result = Vec::new();
8134    let mut stack = vec![node];
8135    while let Some(current) = stack.pop() {
8136        if update_object_macro_field_environment(current, source, fields, ambiguous) {
8137            continue;
8138        }
8139        if matches!(
8140            current.kind(),
8141            "identifier" | "field_identifier" | "type_identifier"
8142        ) && fields.contains_key(node_text(current, source).trim())
8143        {
8144            result.push(current);
8145        }
8146        let mut cursor = current.walk();
8147        let children = current.children(&mut cursor).collect::<Vec<_>>();
8148        stack.extend(children.into_iter().rev());
8149    }
8150    result.sort_by_key(|node| node.start_byte());
8151    result
8152}
8153
8154/// One member region of an aggregate whose head and body tree-sitter collapsed
8155/// into an `ERROR` container, in source order.
8156enum CppCollapsedMember {
8157    /// The members a field-list macro invocation donates, all sharing the
8158    /// invocation's range.
8159    MacroFields {
8160        range: Range,
8161        fields: Vec<MacroReplacementField>,
8162    },
8163    /// A byte span whose ordinary member declarations lost their shape with
8164    /// the aggregate body, with the 1-based line the span starts on.
8165    Declarations {
8166        span: std::ops::Range<usize>,
8167        start_line: usize,
8168    },
8169}
8170
8171/// One aggregate head recovered from the children of a collapsed `ERROR`
8172/// container.
8173///
8174/// A member list that opens with an object-like field-list macro invocation
8175/// has no grammar rule, so the aggregate never forms a specifier and its head
8176/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
8177/// class key, the name, and the opening brace are three consecutive children.
8178/// When the aggregate follows the dangling tail of a comment-truncated
8179/// replacement, the parser reads that tail as the declaration's type and folds
8180/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
8181/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
8182/// }` that follows becomes one `field_declaration` whose class key is an
8183/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
8184/// `initializer_list` (#3098).
8185struct CppCollapsedAggregateHead<'tree> {
8186    key: Node<'tree>,
8187    name: Node<'tree>,
8188    opening: Node<'tree>,
8189    /// The member list when the head folded it into its own node, which also
8190    /// carries the aggregate's closing brace. `None` leaves the aggregate open
8191    /// for the children that follow it.
8192    folded_members: Option<Node<'tree>>,
8193    /// How many children the head consumes.
8194    width: usize,
8195}
8196
8197/// The aggregate head that starts at `children[index]`, if one does.
8198fn cpp_collapsed_aggregate_head<'tree>(
8199    children: &[Node<'tree>],
8200    index: usize,
8201    source: &str,
8202) -> Option<CppCollapsedAggregateHead<'tree>> {
8203    let key = *children.get(index)?;
8204    if matches!(key.kind(), "struct" | "class" | "union") {
8205        let name = *children.get(index + 1)?;
8206        let opening = *children.get(index + 2)?;
8207        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8208            return None;
8209        }
8210        return Some(CppCollapsedAggregateHead {
8211            key,
8212            name,
8213            opening,
8214            folded_members: None,
8215            width: 3,
8216        });
8217    }
8218    cpp_folded_aggregate_head(key, source)
8219}
8220
8221/// The aggregate head tree-sitter folded into one declaration node, reading
8222/// the class key as an `ERROR` and the member list as an initializer.
8223fn cpp_folded_aggregate_head<'tree>(
8224    node: Node<'tree>,
8225    source: &str,
8226) -> Option<CppCollapsedAggregateHead<'tree>> {
8227    if !matches!(node.kind(), "declaration" | "field_declaration") {
8228        return None;
8229    }
8230    let mut cursor = node.walk();
8231    let children = node.children(&mut cursor).collect::<Vec<_>>();
8232    let key_index = children.iter().position(|child| {
8233        child.is_error()
8234            && matches!(
8235                node_text(*child, source).trim(),
8236                "struct" | "class" | "union"
8237            )
8238    })?;
8239    let declarator = *children.get(key_index + 1)?;
8240    // At file scope the grammar has an `init_declarator` for the name and its
8241    // initializer; inside an `ERROR` container the two are loose siblings.
8242    let (name, members) = if declarator.kind() == "init_declarator" {
8243        (
8244            declarator.child_by_field_name("declarator")?,
8245            declarator.child_by_field_name("value")?,
8246        )
8247    } else {
8248        (declarator, *children.get(key_index + 2)?)
8249    };
8250    if !matches!(
8251        name.kind(),
8252        "field_identifier" | "type_identifier" | "identifier"
8253    ) {
8254        return None;
8255    }
8256    if members.kind() != "initializer_list" {
8257        return None;
8258    }
8259    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8260    Some(CppCollapsedAggregateHead {
8261        key: children[key_index],
8262        name,
8263        opening,
8264        folded_members: Some(members),
8265        width: 1,
8266    })
8267}
8268
8269/// Where a folded member list ends: at its own closing brace when it has one,
8270/// so the reparse of the declarations it holds sees a member list and not a
8271/// stray brace.
8272fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8273    let mut cursor = members.walk();
8274    members
8275        .children(&mut cursor)
8276        .filter(|child| child.kind() == "}" && !child.is_missing())
8277        .last()
8278        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8279}
8280
8281/// The members one collapsed aggregate region declares, in source order: what
8282/// its field-list macro invocations donate, then the ordinary declarations
8283/// that follow the last invocation and lost their shape with the body.
8284fn cpp_collapsed_aggregate_members(
8285    macro_nodes: &[Node<'_>],
8286    region: std::ops::Range<usize>,
8287    region_start_line: usize,
8288    source: &str,
8289    environment: &HashMap<String, ObjectMacroReplacement>,
8290) -> Vec<CppCollapsedMember> {
8291    let mut members = macro_nodes
8292        .iter()
8293        .map(|macro_node| CppCollapsedMember::MacroFields {
8294            range: cpp_declaration_range(*macro_node),
8295            fields: object_macro_field_closure(
8296                environment,
8297                &normalize_cpp_whitespace(node_text(*macro_node, source)),
8298            ),
8299        })
8300        .collect::<Vec<_>>();
8301    // Whatever follows the last invocation is an ordinary member list that
8302    // lost its declaration shape with the aggregate's body; the grammar
8303    // recovers it from its own byte range.
8304    let declarations_start = macro_nodes
8305        .last()
8306        .map_or(region.start, |macro_node| macro_node.end_byte());
8307    members.push(CppCollapsedMember::Declarations {
8308        span: declarations_start..region.end,
8309        start_line: region_start_line
8310            + cpp_line_breaks_between(source, region.start, declarations_start),
8311    });
8312    members
8313}
8314
8315/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
8316/// line without rescanning the file from its start.
8317fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8318    source.get(from..to).map_or(0, |slice| {
8319        slice.bytes().filter(|byte| *byte == b'\n').count()
8320    })
8321}
8322
8323/// How many enclosing aggregates a collapsed member declaration closes: the
8324/// close braces it carries that its own braces do not balance. A member that
8325/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
8326/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
8327/// owner early and lost every member after it (#2985).
8328fn count_close_brace_nodes(node: Node<'_>) -> usize {
8329    let mut opened = 0usize;
8330    let mut closed = 0usize;
8331    let mut stack = vec![node];
8332    while let Some(current) = stack.pop() {
8333        if !current.is_missing() {
8334            match current.kind() {
8335                "{" => opened += 1,
8336                "}" => closed += 1,
8337                _ => {}
8338            }
8339        }
8340        let mut cursor = current.walk();
8341        stack.extend(current.children(&mut cursor));
8342    }
8343    closed.saturating_sub(opened)
8344}
8345
8346/// Classify a C++ field while its declaration syntax is already available.
8347///
8348/// The persisted result lets later visibility queries avoid reparsing the
8349/// complete source file only to recover linkage.
8350pub fn cpp_field_declaration_linkage<'tree>(
8351    declaration: Node<'tree>,
8352    source: &str,
8353    ancestry: &ParentIndex<'tree>,
8354) -> CppFieldLinkage {
8355    let mut current = ancestry.parent(declaration);
8356    let mut enclosed_by_class = false;
8357    while let Some(node) = current {
8358        if node.kind() == "namespace_definition"
8359            && node
8360                .child_by_field_name("name")
8361                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8362        {
8363            return CppFieldLinkage::Internal;
8364        }
8365        if matches!(
8366            node.kind(),
8367            "class_specifier" | "struct_specifier" | "union_specifier"
8368        ) && node
8369            .child_by_field_name("name")
8370            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8371        {
8372            return CppFieldLinkage::Internal;
8373        }
8374        if matches!(
8375            node.kind(),
8376            "class_specifier" | "struct_specifier" | "union_specifier"
8377        ) {
8378            enclosed_by_class = true;
8379        }
8380        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8381            return CppFieldLinkage::Internal;
8382        }
8383        current = ancestry.parent(node);
8384    }
8385    if enclosed_by_class {
8386        return CppFieldLinkage::External;
8387    }
8388    let mut cursor = declaration.walk();
8389    let mut has_static = false;
8390    let mut has_extern = false;
8391    let mut has_inline = false;
8392    let mut has_const = false;
8393    let mut has_constexpr = false;
8394    for child in declaration.named_children(&mut cursor) {
8395        let text = normalize_cpp_whitespace(node_text(child, source));
8396        match (child.kind(), text.as_str()) {
8397            ("storage_class_specifier", "static") => has_static = true,
8398            ("storage_class_specifier", "extern") => has_extern = true,
8399            ("storage_class_specifier", "inline") => has_inline = true,
8400            ("storage_class_specifier", "constexpr") => has_constexpr = true,
8401            ("type_qualifier", "const") => has_const = true,
8402            ("type_qualifier", "constexpr") => has_constexpr = true,
8403            _ => {}
8404        }
8405    }
8406    if has_static {
8407        CppFieldLinkage::Internal
8408    } else if has_extern || has_inline {
8409        CppFieldLinkage::External
8410    } else if has_const || has_constexpr {
8411        CppFieldLinkage::InternalUnlessExternalPeer
8412    } else {
8413        CppFieldLinkage::External
8414    }
8415}
8416
8417fn cpp_declaration_range(node: Node<'_>) -> Range {
8418    Range {
8419        start_byte: node.start_byte(),
8420        end_byte: node.end_byte(),
8421        start_line: node.start_position().row + 1,
8422        end_line: node.end_position().row + 1,
8423    }
8424}
8425
8426/// A recovery interval as a [`Range`], for materialization records whose
8427/// window is a byte region rather than one parser node (the sentinel-macro
8428/// region reparses, issue #941/#1657).
8429fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8430    let line_at = |byte: usize| {
8431        source.as_bytes()[..byte]
8432            .iter()
8433            .filter(|&&b| b == b'\n')
8434            .count()
8435            + 1
8436    };
8437    Range {
8438        start_byte,
8439        end_byte,
8440        start_line: line_at(start_byte),
8441        end_line: line_at(end_byte),
8442    }
8443}
8444
8445/// Every `#include` directive the tree holds, in source order.
8446///
8447/// A preorder sweep rather than a step of the declaration walk: the container
8448/// walk descends only through declaration scopes, so an include written inside
8449/// a class body or a function body would otherwise never be seen, and an
8450/// include is an include wherever it is written.
8451pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8452    walk_named_tree_preorder(root, true, |node| {
8453        if node.kind() == "preproc_include" {
8454            let raw = normalize_cpp_whitespace(node_text(node, source));
8455            if !raw.is_empty() {
8456                parsed.imports.push(ImportInfo {
8457                    raw_snippet: raw,
8458                    is_wildcard: false,
8459                    is_global: false,
8460                    identifier: None,
8461                    alias: None,
8462                    path: None,
8463                    binder_span: None,
8464                });
8465            }
8466            return WalkControl::SkipChildren;
8467        }
8468        WalkControl::Continue
8469    });
8470}
8471
8472pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8473    let mut in_block_comment = false;
8474    for line in source.lines() {
8475        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8476        let trimmed = stripped.trim();
8477        if !looks_like_quoted_include_line(trimmed) {
8478            continue;
8479        }
8480
8481        let raw = normalize_cpp_whitespace(trimmed);
8482        // The tree-sitter walk already recorded every `#include` it could see;
8483        // this line scan only recovers the ones a parse error hid, so skip a
8484        // snippet that is already an import binding.
8485        if parsed
8486            .imports
8487            .iter()
8488            .any(|import| import.raw_snippet == raw)
8489        {
8490            continue;
8491        }
8492
8493        parsed.imports.push(ImportInfo {
8494            raw_snippet: raw,
8495            is_wildcard: false,
8496            is_global: false,
8497            identifier: None,
8498            alias: None,
8499            path: None,
8500            binder_span: None,
8501        });
8502    }
8503}
8504
8505fn looks_like_quoted_include_line(line: &str) -> bool {
8506    let Some(rest) = line.trim_start().strip_prefix('#') else {
8507        return false;
8508    };
8509    let Some(rest) = rest.trim_start().strip_prefix("include") else {
8510        return false;
8511    };
8512    rest.trim_start().starts_with('"')
8513}
8514
8515fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8516    let mut raw = Vec::new();
8517    let mut cursor = node.walk();
8518    for child in node.named_children(&mut cursor) {
8519        if child.kind() == "base_class_clause" {
8520            collect_cpp_base_nodes(child, source, &mut raw);
8521        }
8522    }
8523    raw
8524}
8525
8526fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8527    walk_named_tree_preorder(node, false, |child| match child.kind() {
8528        "type_identifier" | "qualified_identifier" | "template_type" => {
8529            let text = normalize_cpp_whitespace(node_text(child, source));
8530            if !text.is_empty() {
8531                raw.push(text);
8532            }
8533            WalkControl::SkipChildren
8534        }
8535        _ => WalkControl::Continue,
8536    });
8537}
8538
8539fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8540    let mut out = String::new();
8541    let chars: Vec<char> = line.chars().collect();
8542    let mut index = 0;
8543    let mut in_string = false;
8544    let mut in_char = false;
8545    let mut escape = false;
8546
8547    while index < chars.len() {
8548        let ch = chars[index];
8549        let next = chars.get(index + 1).copied();
8550
8551        if *in_block_comment {
8552            if ch == '*' && next == Some('/') {
8553                *in_block_comment = false;
8554                index += 2;
8555            } else {
8556                index += 1;
8557            }
8558            continue;
8559        }
8560
8561        if in_string {
8562            out.push(ch);
8563            if escape {
8564                escape = false;
8565            } else if ch == '\\' {
8566                escape = true;
8567            } else if ch == '"' {
8568                in_string = false;
8569            }
8570            index += 1;
8571            continue;
8572        }
8573
8574        if in_char {
8575            out.push(ch);
8576            if escape {
8577                escape = false;
8578            } else if ch == '\\' {
8579                escape = true;
8580            } else if ch == '\'' {
8581                in_char = false;
8582            }
8583            index += 1;
8584            continue;
8585        }
8586
8587        if ch == '/' && next == Some('/') {
8588            break;
8589        }
8590        if ch == '/' && next == Some('*') {
8591            *in_block_comment = true;
8592            index += 2;
8593            continue;
8594        }
8595        if ch == '"' {
8596            in_string = true;
8597            out.push(ch);
8598            index += 1;
8599            continue;
8600        }
8601        if ch == '\'' {
8602            in_char = true;
8603            out.push(ch);
8604            index += 1;
8605            continue;
8606        }
8607
8608        out.push(ch);
8609        index += 1;
8610    }
8611
8612    out
8613}
8614
8615#[derive(Clone)]
8616struct FunctionInfo {
8617    package_name: String,
8618    owner: Option<CppMemberOwner>,
8619    name: String,
8620    signature: String,
8621}
8622
8623/// Owner of a member function, kept structured so a literal `$` inside a
8624/// source-level class name never crosses a join/split boundary: the legacy
8625/// `$`-joined owner string was re-split at fq construction, dropping a leading
8626/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
8627/// and tripping the package/short boundary assert -- the #2140 corruption one
8628/// level up (#2362).
8629#[derive(Clone)]
8630enum CppMemberOwner {
8631    /// Source-level owner class chain from a qualified declarator-id, one
8632    /// class name per component (`Outer::Inner::method` -> `["Outer",
8633    /// "Inner"]`); each component may itself contain a literal `$`.
8634    Chain(Vec<String>),
8635    /// The lexically enclosing or recovered class unit; the member fq extends
8636    /// its fq directly instead of re-splitting its `$`-joined short chain.
8637    Unit(CodeUnit),
8638}
8639
8640impl CppMemberOwner {
8641    /// The legacy `$`-joined owner chain used in the member's short name.
8642    fn short_chain(&self) -> String {
8643        match self {
8644            Self::Chain(chain) => chain.join("$"),
8645            Self::Unit(parent) => parent.short_name().to_string(),
8646        }
8647    }
8648}
8649
8650enum DeclaratorKind<'a> {
8651    Function(Node<'a>),
8652    Variable(Node<'a>),
8653}
8654
8655impl FunctionInfo {
8656    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8657        self.code_unit_with_synthetic(file, false)
8658    }
8659
8660    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8661        let short_name = match &self.owner {
8662            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8663            None => self.name.clone(),
8664        };
8665        let fq = match &self.owner {
8666            Some(CppMemberOwner::Chain(chain)) => {
8667                debug_assert!(
8668                    !chain.is_empty(),
8669                    "an empty owner chain is no owner; producers return None instead"
8670                );
8671                let mut fq = FqName::new();
8672                cpp_push_package(&mut fq, &self.package_name);
8673                let mut first = true;
8674                for component in chain {
8675                    let kind = if first {
8676                        SegmentKind::Type
8677                    } else {
8678                        SegmentKind::Nested
8679                    };
8680                    fq.push(cpp_segment(component, kind));
8681                    first = false;
8682                }
8683                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8684                fq
8685            }
8686            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8687                .fq()
8688                .clone()
8689                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8690            // An anonymous parent (empty short chain) contributes no owner
8691            // segment -- the same guard as cpp_join_member_short above.
8692            Some(CppMemberOwner::Unit(_)) | None => {
8693                let mut fq = FqName::new();
8694                cpp_push_package(&mut fq, &self.package_name);
8695                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8696                fq
8697            }
8698        };
8699        CodeUnit::with_signature_and_fq(
8700            file,
8701            CodeUnitType::Function,
8702            self.package_name.clone(),
8703            short_name,
8704            Some(self.signature.clone()),
8705            synthetic,
8706            fq,
8707        )
8708    }
8709}
8710
8711fn extract_function_info(
8712    declarator: Node<'_>,
8713    source: &str,
8714    scope: &ScopeInfo,
8715) -> Option<FunctionInfo> {
8716    let parameters_node = declarator.child_by_field_name("parameters")?;
8717    let declarator_name_node = declarator
8718        .child_by_field_name("declarator")
8719        .or_else(|| parameters_node.prev_named_sibling())?;
8720    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8721}
8722
8723fn extract_function_info_from_name(
8724    declarator: Node<'_>,
8725    declarator_name_node: Node<'_>,
8726    source: &str,
8727    scope: &ScopeInfo,
8728) -> Option<FunctionInfo> {
8729    let parameters_node = declarator.child_by_field_name("parameters")?;
8730    let parameters_text = cpp_parameter_signature(parameters_node, source);
8731    let recovered_specialization_member = scope
8732        .recovered_specialization_member_scope
8733        .then(|| {
8734            let terminal = declarator_name_node
8735                .child_by_field_name("name")
8736                .unwrap_or(declarator_name_node);
8737            let name = canonical_cpp_qualified_component(terminal, source)?.name;
8738            let owner = scope.class_unit.as_ref()?;
8739            Some((
8740                Some(CppMemberOwner::Unit(owner.clone())),
8741                name,
8742                scope.package_name.clone(),
8743            ))
8744        })
8745        .flatten();
8746    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8747        parts
8748    } else if let Some(parts) =
8749        split_structured_templated_cpp_name(declarator_name_node, source, scope)
8750    {
8751        parts
8752    } else {
8753        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8754            declarator_name_node,
8755            source,
8756        )?);
8757        if raw_name.is_empty() {
8758            return None;
8759        }
8760        split_cpp_name(&raw_name, scope)
8761    };
8762    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8763    let mut signature = if suffix.is_empty() {
8764        parameters_text
8765    } else {
8766        format!("{parameters_text} {suffix}")
8767    };
8768    if let Some(template_signature) = &scope.template_signature {
8769        signature = format!("{template_signature}{signature}");
8770    }
8771
8772    Some(FunctionInfo {
8773        package_name,
8774        owner,
8775        name,
8776        signature,
8777    })
8778}
8779
8780/// Recover the semantic return type and callable name when a declaration macro
8781/// occupies a function definition's `type` field. Tree-sitter either exposes a
8782/// scalar return as the declarator's apparent name and the callable as the sole
8783/// identifier in an `ERROR`, or joins a template return and callable into a
8784/// qualified identifier with a missing `::`. Both shapes retain the complete
8785/// parameter list and body; a concrete separator remains an out-of-line member.
8786fn cpp_macro_displaced_callable_parts<'tree>(
8787    function_declarator: Node<'tree>,
8788    source: &str,
8789    ancestry: &ParentIndex<'tree>,
8790) -> Option<(Node<'tree>, Node<'tree>)> {
8791    let definition = ancestry.parent(function_declarator)?;
8792    if definition.kind() != "function_definition"
8793        || definition.child_by_field_name("declarator") != Some(function_declarator)
8794        || definition
8795            .child_by_field_name("body")
8796            .is_none_or(|body| body.kind() != "compound_statement")
8797    {
8798        return None;
8799    }
8800    let macro_type = definition.child_by_field_name("type")?;
8801    if macro_type.kind() != "type_identifier"
8802        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8803    {
8804        return None;
8805    }
8806
8807    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
8808    if apparent_return_type.kind() == "qualified_identifier"
8809        && let (Some(return_type), Some(callable_name)) = (
8810            apparent_return_type.child_by_field_name("scope"),
8811            apparent_return_type.child_by_field_name("name"),
8812        )
8813        && return_type.kind() == "template_type"
8814        && matches!(callable_name.kind(), "identifier" | "field_identifier")
8815        && (0..apparent_return_type.child_count())
8816            .filter_map(|index| apparent_return_type.child(index))
8817            .any(|child| child.kind() == "::" && child.is_missing())
8818        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
8819        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8820    {
8821        return Some((return_type, callable_name));
8822    }
8823    if !matches!(
8824        apparent_return_type.kind(),
8825        "identifier" | "field_identifier" | "type_identifier"
8826    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
8827    {
8828        return None;
8829    }
8830    let parameters = function_declarator.child_by_field_name("parameters")?;
8831    let mut cursor = function_declarator.walk();
8832    let between = function_declarator
8833        .named_children(&mut cursor)
8834        .filter(|child| child.kind() != "comment")
8835        .filter(|child| {
8836            child.start_byte() >= apparent_return_type.end_byte()
8837                && child.end_byte() <= parameters.start_byte()
8838                && !same_node(*child, apparent_return_type)
8839                && !same_node(*child, parameters)
8840        })
8841        .collect::<Vec<_>>();
8842    let [name_error] = between.as_slice() else {
8843        return None;
8844    };
8845    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
8846        return None;
8847    }
8848    let callable_name = name_error.named_child(0)?;
8849    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
8850        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
8851    {
8852        return None;
8853    }
8854    Some((apparent_return_type, callable_name))
8855}
8856
8857/// The part of a `function_declarator` after its parameter list that belongs to
8858/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
8859/// specification, a trailing return type and a trailing requires-clause.
8860///
8861/// The grammar makes each of these a distinct sibling of the `parameters`
8862/// field, so they are read from the tree. Splitting the declarator's text on
8863/// the parameter list instead silently dropped every qualifier whenever the
8864/// parameter list was spelled with whitespace that normalization rewrote - a
8865/// line break or a double space was enough to make a `const` member definition
8866/// a different logical symbol from its declaration (#1827).
8867///
8868/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
8869/// are deliberately excluded. C++ does not make them part of the signature and
8870/// an out-of-line definition never repeats them, so including them would split
8871/// a declaration from its own definition.
8872fn cpp_declarator_identity_suffix(
8873    declarator: Node<'_>,
8874    parameters_node: Node<'_>,
8875    source: &str,
8876) -> String {
8877    let mut cursor = declarator.walk();
8878    let parts = declarator
8879        .named_children(&mut cursor)
8880        .filter(|child| child.start_byte() >= parameters_node.end_byte())
8881        .filter(|child| {
8882            matches!(
8883                child.kind(),
8884                "type_qualifier"
8885                    | "ref_qualifier"
8886                    | "noexcept"
8887                    | "throw_specifier"
8888                    | "trailing_return_type"
8889                    | "requires_clause"
8890            )
8891        })
8892        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8893        .filter(|text| !text.is_empty())
8894        .collect::<Vec<_>>();
8895    normalize_cpp_qualifier_suffix(&parts.join(" "))
8896}
8897
8898/// The identity suffix of one callable declarator, for a consumer that holds
8899/// the declarator rather than the declaration walk's parts.
8900///
8901/// The persisted signature concatenates the parameter spelling and this suffix,
8902/// so a comparison that must agree on the suffix alone recomputes it here
8903/// instead of splitting the stored string.
8904pub(crate) fn cpp_callable_identity_suffix(
8905    function_declarator: Node<'_>,
8906    source: &str,
8907) -> Option<String> {
8908    let parameters_node = function_declarator.child_by_field_name("parameters")?;
8909    Some(cpp_declarator_identity_suffix(
8910        function_declarator,
8911        parameters_node,
8912        source,
8913    ))
8914}
8915
8916pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
8917    match classify_declarator(node)? {
8918        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
8919        DeclaratorKind::Variable(_) => None,
8920    }
8921}
8922
8923fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
8924    match node.kind() {
8925        "function_declarator" => {
8926            let inner = node
8927                .child_by_field_name("declarator")
8928                .or_else(|| node.child_by_field_name("name"))
8929                .or_else(|| last_named_child(node));
8930            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
8931                Some(DeclaratorKind::Variable(node))
8932            } else {
8933                Some(DeclaratorKind::Function(node))
8934            }
8935        }
8936        "init_declarator"
8937        | "pointer_declarator"
8938        | "reference_declarator"
8939        | "parenthesized_declarator"
8940        | "array_declarator"
8941        | "attributed_declarator"
8942        | "template_function" => node
8943            .child_by_field_name("declarator")
8944            .or_else(|| node.child_by_field_name("name"))
8945            .or_else(|| last_named_child(node))
8946            .and_then(classify_declarator),
8947        "identifier" | "field_identifier" | "qualified_identifier" => {
8948            Some(DeclaratorKind::Variable(node))
8949        }
8950        _ => node
8951            .child_by_field_name("declarator")
8952            .or_else(|| node.child_by_field_name("name"))
8953            .or_else(|| last_named_child(node))
8954            .and_then(classify_declarator),
8955    }
8956}
8957
8958fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
8959    matches!(
8960        node.kind(),
8961        "function_declarator"
8962            | "init_declarator"
8963            | "pointer_declarator"
8964            | "reference_declarator"
8965            | "parenthesized_declarator"
8966            | "array_declarator"
8967            | "attributed_declarator"
8968            | "template_function"
8969            | "identifier"
8970            | "field_identifier"
8971            | "qualified_identifier"
8972    )
8973}
8974
8975fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
8976    let class_like = first_class_like_child(node);
8977    let mut cursor = node.walk();
8978    node.named_children(&mut cursor).any(|child| {
8979        matches!(
8980            child.kind(),
8981            "init_declarator"
8982                | "pointer_declarator"
8983                | "reference_declarator"
8984                | "array_declarator"
8985                | "function_declarator"
8986                | "parenthesized_declarator"
8987                | "attributed_declarator"
8988        ) || matches!(
8989            child.kind(),
8990            "identifier" | "field_identifier" | "qualified_identifier"
8991        ) && class_like.is_none_or(|class_node| {
8992            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
8993        })
8994    })
8995}
8996
8997/// One namespace-scope forward class declaration that a recovered export-macro
8998/// class definition may borrow its identity from.  Tree-sitter can close a
8999/// malformed class at the enclosing namespace's closing brace, leaving the later
9000/// class definitions as root-level recovered `function_definition` nodes.  A
9001/// preceding `class Name;` in the same namespace is the only structured identity
9002/// signal available in that shape.
9003///
9004/// Everything recorded here is a property of the forward declaration alone.
9005/// What depends on the node doing the asking -- that the forward and its
9006/// namespace both close before it, with nothing but recovery trivia between --
9007/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
9008/// tree answers every later question about it.
9009struct CppNamespaceForward {
9010    name: String,
9011    start_byte: usize,
9012    /// Where the malformed namespace that held the forward ended.  No query
9013    /// asks anything else about that node.
9014    namespace_end_byte: usize,
9015    package_name: String,
9016}
9017
9018/// Read `node` as a borrowable namespace forward declaration.
9019///
9020/// The admission is deliberately conservative: only a body-less class specifier
9021/// whose declaration has no declarator, at namespace scope rather than inside a
9022/// function or class body, in a namespace that itself failed to parse.
9023fn cpp_namespace_forward_entry<'tree>(
9024    node: Node<'tree>,
9025    source: &str,
9026    ancestry: &ParentIndex<'tree>,
9027) -> Option<CppNamespaceForward> {
9028    if !matches!(
9029        node.kind(),
9030        "class_specifier" | "struct_specifier" | "union_specifier"
9031    ) || cpp_body_node(node).is_some()
9032    {
9033        return None;
9034    }
9035    let parent = node.parent()?;
9036    if !(parent.kind() == "declaration_list"
9037        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9038    {
9039        return None;
9040    }
9041    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9042    // Borrowing is only justified by the parser-recovery shape we are
9043    // repairing: the namespace that held the forward must itself contain a
9044    // syntax error. A clean, unrelated namespace forward is not an identity
9045    // proof.
9046    if !namespace.has_error() {
9047        return None;
9048    }
9049    Some(CppNamespaceForward {
9050        name: class_like_name(node, source, ancestry)?,
9051        start_byte: node.start_byte(),
9052        namespace_end_byte: namespace.end_byte(),
9053        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9054    })
9055}
9056
9057/// Whether `forward` stands in the recovery relation to the node asking about
9058/// it: it and its malformed namespace both closed before the recovered class,
9059/// and nothing but recovery trivia separates the two.
9060fn cpp_namespace_forward_matches_recovery(
9061    forward: &CppNamespaceForward,
9062    recovered_node: Node<'_>,
9063) -> bool {
9064    forward.start_byte < recovered_node.start_byte()
9065        && forward.namespace_end_byte < recovered_node.start_byte()
9066        && malformed_namespace_is_nearest_recovery_region(
9067            forward.namespace_end_byte,
9068            recovered_node,
9069        )
9070}
9071
9072/// What one open [`CppVisitor::record_recovered_declarations`] has watched
9073/// happen to the declaration set.
9074///
9075/// The recovered set used to be a difference against a clone of the whole
9076/// declaration set, taken once per recovery: O(recoveries x declarations) on
9077/// exactly the error-recovered files that already walk slowest (#2787). The
9078/// walk knows which declarations it creates, so the capture collects them as
9079/// they are made and the difference is never needed.
9080///
9081/// `removed_pre_existing` is what makes that equal to the difference. A
9082/// deferred replacement removes the replaced declaration's children
9083/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
9084/// re-creates them. Creation alone cannot tell that apart from a first mint, so
9085/// a removal of a declaration this capture did not create records that it was
9086/// already there when the capture opened.
9087#[derive(Debug, Default)]
9088pub struct CppRecoveryCapture {
9089    /// Declarations created while this capture was open, in creation order.
9090    created: Vec<CodeUnit>,
9091    /// Membership for `created`.
9092    created_units: HashSet<CodeUnit>,
9093    /// Declarations that predate this capture and have been removed during it.
9094    removed_pre_existing: HashSet<CodeUnit>,
9095}
9096
9097/// Which owners the parse product already holds field declarations for, folded
9098/// in as the walk records them.
9099///
9100/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
9101/// and used to answer it by scanning every declaration accumulated so far:
9102/// O(enums x declarations) on exactly the generated headers that declare many
9103/// of both (#2786). The answer only grows by declaration, so the walk carries
9104/// it. A field's short name names its owner chain, `Owner.member`, so one field
9105/// answers for every dotted prefix of its own short name; an anonymous enum's
9106/// or union's enumerators carry bare short names instead (#2140), which is what
9107/// an empty owner short name asks about.
9108#[derive(Debug, Default)]
9109pub struct CppFieldOwnerIndex {
9110    /// Package name -> the owner short names its fields name.
9111    owners: HashMap<String, HashSet<String>>,
9112    /// Packages holding at least one field that names no owner.
9113    ownerless_packages: HashSet<String>,
9114}
9115
9116impl CppFieldOwnerIndex {
9117    /// The index of the declarations recorded so far, built when the first
9118    /// question arrives.
9119    fn of<'unit>(
9120        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9121        file: &ProjectFile,
9122    ) -> Self {
9123        let mut index = Self::default();
9124        for declaration in declarations {
9125            index.record(declaration, file);
9126        }
9127        index
9128    }
9129
9130    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9131        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9132            return;
9133        }
9134        let short_name = code_unit.short_name();
9135        let package_name = code_unit.package_name();
9136        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9137            self.ownerless_packages.insert(package_name.to_string());
9138        }
9139        if !short_name.contains('.') {
9140            return;
9141        }
9142        if !self.owners.contains_key(package_name) {
9143            self.owners
9144                .insert(package_name.to_string(), HashSet::default());
9145        }
9146        let owners = self
9147            .owners
9148            .get_mut(package_name)
9149            .expect("the package entry was just ensured");
9150        for (offset, _) in short_name.match_indices('.') {
9151            let owner = &short_name[..offset];
9152            if !owners.contains(owner) {
9153                owners.insert(owner.to_string());
9154            }
9155        }
9156    }
9157
9158    /// Whether a field declaration in `package_name` names `owner_short_name`
9159    /// as its owner. An empty owner asks about ownerless fields instead.
9160    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9161        if owner_short_name.is_empty() {
9162            self.ownerless_packages.contains(package_name)
9163        } else {
9164            self.owners
9165                .get(package_name)
9166                .is_some_and(|owners| owners.contains(owner_short_name))
9167        }
9168    }
9169}
9170
9171/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
9172/// debug build asserts every carried answer against and as the release-mode
9173/// parity tests' reference (#2786).
9174#[cfg(any(debug_assertions, test))]
9175fn cpp_declarations_hold_owned_fields<'unit>(
9176    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9177    file: &ProjectFile,
9178    package_name: &str,
9179    owner_short_name: &str,
9180) -> bool {
9181    let prefix = format!("{owner_short_name}.");
9182    declarations.into_iter().any(|unit| {
9183        unit.kind() == CodeUnitType::Field
9184            && unit.source() == file
9185            && unit.package_name() == package_name
9186            && if owner_short_name.is_empty() {
9187                // Anonymous enum/union parent: its enumerators carry bare
9188                // short names (#2140), so presence means any ownerless
9189                // field in this file.
9190                !unit.short_name().contains(['.', '$'])
9191            } else {
9192                unit.short_name().starts_with(&prefix)
9193            }
9194    })
9195}
9196
9197/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
9198///
9199/// A region reparse is its own tree and is dropped while the walk that made it
9200/// continues, so a later parse can be allocated at the same address and hand out
9201/// the same node ids.  The root's span and shape pin the identity its address
9202/// alone does not: two roots agreeing on all of this are the same parse of the
9203/// same bytes, and a scan of one is a scan of the other.
9204#[derive(PartialEq, Eq, Hash)]
9205pub struct CppTreeIdentity {
9206    root_id: usize,
9207    start_byte: usize,
9208    end_byte: usize,
9209    kind_id: u16,
9210    child_count: usize,
9211}
9212
9213impl CppTreeIdentity {
9214    fn of(root: Node<'_>) -> Self {
9215        Self {
9216            root_id: root.id(),
9217            start_byte: root.start_byte(),
9218            end_byte: root.end_byte(),
9219            kind_id: root.kind_id(),
9220            child_count: root.child_count(),
9221        }
9222    }
9223}
9224
9225/// The namespace forward declarations one tree's prefix holds, folded in as the
9226/// walk asks about them.
9227///
9228/// `scope_for_recovered_exported_class` asks the same question once per
9229/// recovered class, and the answer depends only on the part of the tree that
9230/// starts before the asking node.  Rescanning that prefix per question is
9231/// quadratic in the file, and a generated header whose parse recovery leaves
9232/// thousands of class-like declarations at file scope pays all of it: 1,904
9233/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
9234/// (#2754).  This carries the scan forward instead.  Each question advances the
9235/// traversal from wherever the last one stopped to the asking node's start byte,
9236/// so a whole walk pays at most one pass over the prefix its furthest question
9237/// reaches, and each question then costs a name lookup.
9238#[derive(Default)]
9239pub struct CppNamespaceForwardScan {
9240    /// Every named node starting before this byte has been folded in.
9241    scanned_through: usize,
9242    forwards: HashMap<String, Vec<CppNamespaceForward>>,
9243}
9244
9245impl CppNamespaceForwardScan {
9246    /// Fold in every named node of `root` that starts at or after the fold
9247    /// watermark and before `cutoff`.
9248    ///
9249    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
9250    /// pass owes are exactly the ones no earlier pass reached, and a question
9251    /// about an earlier byte than one already answered costs nothing.
9252    fn advance_to<'tree>(
9253        &mut self,
9254        root: Node<'tree>,
9255        cutoff: usize,
9256        source: &str,
9257        ancestry: &ParentIndex<'tree>,
9258    ) {
9259        if cutoff <= self.scanned_through {
9260            return;
9261        }
9262        let folded_through = self.scanned_through;
9263        let mut cursor = root.walk();
9264        let mut stack = vec![root];
9265        while let Some(current) = stack.pop() {
9266            if (folded_through..cutoff).contains(&current.start_byte())
9267                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9268            {
9269                self.forwards
9270                    .entry(forward.name.clone())
9271                    .or_default()
9272                    .push(forward);
9273            }
9274            // A subtree ending before the watermark holds only nodes an earlier
9275            // pass already folded, and one starting at or after the cutoff is
9276            // outside the prefix being asked about. Skipping both is what keeps
9277            // the total traversal to one pass.
9278            for child in current.named_children(&mut cursor) {
9279                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9280                    stack.push(child);
9281                }
9282            }
9283        }
9284        self.scanned_through = cutoff;
9285    }
9286
9287    /// The one namespace `name` was forward declared in before `recovered_node`.
9288    /// More than one matching forward declaration is ambiguous and answers
9289    /// nothing rather than guessing.
9290    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9291        let mut matching = self
9292            .forwards
9293            .get(name)
9294            .into_iter()
9295            .flatten()
9296            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9297        let first = matching.next()?;
9298        matching
9299            .next()
9300            .is_none()
9301            .then(|| first.package_name.clone())
9302    }
9303}
9304
9305/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
9306/// debug build checks every answer against (and the one the parity tests drive
9307/// directly).  It walks the whole prefix per question, which is exactly the cost
9308/// #2754 removed from the release path.
9309#[cfg(any(debug_assertions, test))]
9310fn unique_earlier_cpp_namespace_forward<'tree>(
9311    recovered_node: Node<'tree>,
9312    name: &str,
9313    source: &str,
9314    ancestry: &ParentIndex<'tree>,
9315) -> Option<String> {
9316    let mut root = recovered_node;
9317    while let Some(parent) = ancestry.parent(root) {
9318        root = parent;
9319    }
9320
9321    let mut candidates = Vec::new();
9322    let mut stack = vec![root];
9323    while let Some(current) = stack.pop() {
9324        if current.start_byte() < recovered_node.start_byte()
9325            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9326            && forward.name == name
9327            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9328        {
9329            candidates.push(forward.package_name);
9330        }
9331
9332        let mut cursor = current.walk();
9333        for child in current.named_children(&mut cursor) {
9334            if child.start_byte() < recovered_node.start_byte() {
9335                stack.push(child);
9336            }
9337        }
9338    }
9339
9340    if candidates.len() == 1 {
9341        candidates.pop()
9342    } else {
9343        None
9344    }
9345}
9346
9347fn malformed_namespace_is_nearest_recovery_region(
9348    namespace_end_byte: usize,
9349    recovered_node: Node<'_>,
9350) -> bool {
9351    let mut root = recovered_node;
9352    while let Some(parent) = root.parent() {
9353        root = parent;
9354    }
9355    let mut cursor = root.walk();
9356    root.named_children(&mut cursor)
9357        .filter(|sibling| {
9358            namespace_end_byte <= sibling.start_byte()
9359                && sibling.end_byte() <= recovered_node.start_byte()
9360        })
9361        .all(is_malformed_namespace_recovery_trivia)
9362}
9363
9364fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9365    matches!(node.kind(), "ERROR" | "comment")
9366        || node.kind().starts_with("preproc_")
9367        || node.kind() == "expression_statement" && node.named_child_count() == 0
9368}
9369
9370/// Return the namespace path for a forward class only when the declaration is
9371/// at namespace scope.  A declaration nested in a function/class body may share
9372/// the same namespace ancestor but cannot identify a top-level class definition.
9373fn cpp_namespace_name_for_forward<'tree>(
9374    node: Node<'tree>,
9375    source: &str,
9376    ancestry: &ParentIndex<'tree>,
9377) -> Option<String> {
9378    cpp_namespace_definition_for_forward(node, ancestry)?;
9379    cpp_lexical_namespace_name(node, source, ancestry)
9380}
9381
9382fn cpp_namespace_definition_for_forward<'tree>(
9383    node: Node<'tree>,
9384    ancestry: &ParentIndex<'tree>,
9385) -> Option<Node<'tree>> {
9386    let declaration = ancestry.parent(node)?;
9387    let mut ancestor = ancestry.parent(declaration);
9388    while let Some(current) = ancestor {
9389        if matches!(
9390            current.kind(),
9391            "compound_statement"
9392                | "field_declaration_list"
9393                | "class_specifier"
9394                | "struct_specifier"
9395                | "union_specifier"
9396                | "function_definition"
9397                | "lambda_expression"
9398        ) {
9399            return None;
9400        }
9401        if current.kind() == "namespace_definition" {
9402            return Some(current);
9403        }
9404        ancestor = ancestry.parent(current);
9405    }
9406    None
9407}
9408
9409fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9410    match node.kind() {
9411        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9412        "parenthesized_declarator" => node
9413            .child_by_field_name("declarator")
9414            .or_else(|| last_named_child(node))
9415            .is_some_and(is_pointer_wrapper_declarator),
9416        "template_function" => node
9417            .child_by_field_name("name")
9418            .is_some_and(is_function_pointer_like_inner_declarator),
9419        _ => false,
9420    }
9421}
9422
9423fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9424    match node.kind() {
9425        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9426        "parenthesized_declarator" => node
9427            .child_by_field_name("declarator")
9428            .or_else(|| last_named_child(node))
9429            .is_some_and(is_pointer_wrapper_declarator),
9430        _ => false,
9431    }
9432}
9433
9434fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9435    let cleaned = raw_name.trim_start_matches("template ").trim();
9436    // A leading `::` is the explicit-global marker, not an empty owner segment.
9437    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
9438    // erroneous macro envelope swallowing the first identifier of an
9439    // out-of-line `X::X` constructor, chromium #1573); without this strip the
9440    // split below yields owner_parts `[""]`, constructing a unit with an empty
9441    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
9442    let cleaned = cleaned.trim_start_matches("::");
9443    // Parser recovery can preserve two adjacent scope operators around a
9444    // missing component (for example `X::/**/::method` in compiler diagnostic
9445    // fixtures). Empty components are syntax-recovery artifacts, never C++
9446    // owners. Keeping one as the final owner constructed `short_name=".method"`
9447    // and violated the structured package/short boundary during a large LLVM
9448    // workspace build. This is the same legacy-string-to-FqName bridge as the
9449    // ordinary split above; discard only components that the delimiter itself
9450    // proves empty.
9451    let parts: Vec<_> = cleaned
9452        .split("::")
9453        .filter(|component| !component.is_empty())
9454        .collect();
9455    if parts.is_empty() {
9456        return (None, cleaned.to_string(), scope.package_name.clone());
9457    }
9458    if parts.len() > 1 {
9459        let name = parts.last().unwrap_or(&cleaned).to_string();
9460        let owner_parts = &parts[..parts.len() - 1];
9461        if let Some(class_unit) = &scope.class_unit {
9462            // Lexically inside a class body: the owner is that class, whatever
9463            // the declarator re-qualifies it as.
9464            return (
9465                Some(CppMemberOwner::Unit(class_unit.clone())),
9466                name,
9467                scope.package_name.clone(),
9468            );
9469        }
9470        if !scope.package_name.is_empty() {
9471            // Out-of-line member definition written *inside* an enclosing
9472            // `namespace {}` block (scope package is that namespace). Every
9473            // owner segment before the terminal member is a class-nesting step
9474            // -- an out-of-line nested-class member `Outer::Inner::method` in
9475            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
9476            // namespace path: `using namespace` never brings nested-class
9477            // access into unqualified scope, so C++ always writes the full
9478            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
9479            // that redundantly re-states the enclosing namespace it already
9480            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
9481            // strip that re-qualifying prefix (which duplicates a suffix of the
9482            // enclosing package path) before treating what remains as the
9483            // nested-class chain, so the redundant spelling still lands on the
9484            // same `log4cxx.Foo.method` identity as its header declaration.
9485            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9486            let owner = (!nested.is_empty()).then(|| {
9487                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9488            });
9489            return (owner, name, scope.package_name.clone());
9490        }
9491        // File scope (no enclosing `namespace {}` block, scope package empty).
9492        let (owner, package_name) = if owner_parts.len() > 1 {
9493            // A multi-segment qualifier at file scope with no enclosing
9494            // namespace: treat all but the last owner segment as the namespace
9495            // path and the last as the owning class (`ns1::ns2::Class::method`
9496            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
9497            // is really a namespace or an outer class cannot be told from the
9498            // declarator text alone here, and no enclosing namespace or
9499            // in-index owner is available at per-file extraction to confirm the
9500            // class reading, so the far-more-common namespace interpretation is
9501            // kept rather than guessed away (the nested-class-at-file-scope and
9502            // using-directive-qualified nested-class shapes remain on this
9503            // behavior; see #1121).
9504            (
9505                Some(CppMemberOwner::Chain(vec![
9506                    owner_parts.last().unwrap_or(&"").to_string(),
9507                ])),
9508                owner_parts[..owner_parts.len() - 1].join("::"),
9509            )
9510        } else {
9511            // A bare `Class::member` qualifier at file scope carries no
9512            // namespace segment of its own. The declarator alone cannot say
9513            // which namespace owns `Class` -- but a `using namespace X;`
9514            // directive already in effect at this point in the file (#1093,
9515            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
9516            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
9517            // is the remaining structural signal for it, so fall back to it
9518            // rather than leaving the definition's package empty while its
9519            // header declaration (parsed inside the `namespace {}` block) keeps
9520            // the real one -- an identity split that made the same member
9521            // unresolvable under its own displayed spelling.
9522            (
9523                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9524                cpp_using_directive_namespace_for_bare_owner(scope),
9525            )
9526        };
9527        return (owner, name, package_name);
9528    }
9529
9530    let package_name = scope.package_name.clone();
9531    let owner = scope
9532        .class_unit
9533        .as_ref()
9534        .map(|parent| CppMemberOwner::Unit(parent.clone()));
9535    (owner, cleaned.to_string(), package_name)
9536}
9537
9538/// Drop the leading owner segments of an out-of-line member qualifier that
9539/// merely re-state the enclosing namespace the definition already sits in, so
9540/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
9541/// definition may redundantly write `a::b::Outer::Inner::method` (or the
9542/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
9543/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
9544/// noise, not class-nesting steps. Returns the owner segments with the longest
9545/// such re-qualifying prefix removed (possibly all of them, when the qualifier
9546/// names only the enclosing namespace before the terminal member -- a
9547/// re-qualified free function). `package_name` is the enclosing namespace path
9548/// in its stored `::`-joined form; both sides are split on the same delimiter
9549/// the namespace walker joined them with, so this compares namespace *segments*
9550/// rather than scanning text.
9551fn strip_redundant_namespace_prefix<'a>(
9552    owner_parts: &'a [&'a str],
9553    package_name: &str,
9554) -> &'a [&'a str] {
9555    if package_name.is_empty() {
9556        return owner_parts;
9557    }
9558    let package_segments: Vec<&str> = package_name.split("::").collect();
9559    let max_prefix = owner_parts.len().min(package_segments.len());
9560    for prefix_len in (1..=max_prefix).rev() {
9561        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9562        if &owner_parts[..prefix_len] == package_suffix {
9563            return &owner_parts[prefix_len..];
9564        }
9565    }
9566    owner_parts
9567}
9568
9569/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
9570/// class name at file/namespace scope, from the `using namespace` directives
9571/// visible at this point in the file. Several may be in scope at once (a
9572/// primary `using namespace NS;` alongside deeper conveniences like `using
9573/// namespace NS::helpers;`); since the declarator gives no way to tell which
9574/// one actually declares the owner class, prefer the shallowest (fewest
9575/// `::`-separated segments) as the file's most likely "home" namespace,
9576/// breaking ties by declaration order. Returns an empty string (leaving the
9577/// caller's package unqualified, as before) when no using-namespace directive
9578/// is in scope.
9579fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9580    scope
9581        .visible_using_namespaces
9582        .iter()
9583        .min_by_key(|namespace| namespace.split("::").count())
9584        .cloned()
9585        .unwrap_or_default()
9586}
9587
9588struct CppQualifiedNameComponent {
9589    name: String,
9590    is_template_id: bool,
9591}
9592
9593/// Canonical nested-class chain for an out-of-line class definition written
9594/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
9595/// component per class (`["Outer", "Inner"]`).
9596///
9597/// The enclosing namespace fixes the namespace/class boundary: after an
9598/// optional redundant spelling of that namespace, every component belongs to
9599/// the class chain. File-scope qualified class names remain untouched because
9600/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
9601///
9602/// The components stay structured (rather than being `$`-joined here) so the
9603/// fq construction can push one Type/Nested segment per class; the `$`-joined
9604/// short-name display form is derived at the call sites that need it.
9605fn qualified_class_name_chain(
9606    class_node: Node<'_>,
9607    source: &str,
9608    scope: &ScopeInfo,
9609) -> Option<Vec<String>> {
9610    if scope.package_name.is_empty() || scope.class_unit.is_some() {
9611        return None;
9612    }
9613    let name = class_node.child_by_field_name("name")?;
9614    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9615    if explicitly_global
9616        || components.len() < 2
9617        || components.iter().any(|component| component.is_template_id)
9618    {
9619        return None;
9620    }
9621    let names = components
9622        .iter()
9623        .map(|component| component.name.as_str())
9624        .collect::<Vec<_>>();
9625    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9626    if class_chain.is_empty() {
9627        return None;
9628    }
9629    Some(class_chain.iter().map(|name| name.to_string()).collect())
9630}
9631
9632fn structured_cpp_qualified_components(
9633    qualified_name: Node<'_>,
9634    source: &str,
9635) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9636    if qualified_name.kind() != "qualified_identifier" {
9637        return None;
9638    }
9639
9640    let mut components = Vec::new();
9641    let mut current = qualified_name;
9642    let mut explicitly_global = false;
9643    loop {
9644        if current.kind() == "qualified_identifier" {
9645            if let Some(component) = current.child_by_field_name("scope") {
9646                components.push(canonical_cpp_qualified_component(component, source)?);
9647            } else if components.is_empty() {
9648                explicitly_global = true;
9649            } else {
9650                return None;
9651            }
9652            current = current.child_by_field_name("name")?;
9653        } else {
9654            components.push(canonical_cpp_qualified_component(current, source)?);
9655            break;
9656        }
9657    }
9658    Some((components, explicitly_global))
9659}
9660
9661fn split_structured_templated_cpp_name(
9662    declarator_name: Node<'_>,
9663    source: &str,
9664    scope: &ScopeInfo,
9665) -> Option<(Option<CppMemberOwner>, String, String)> {
9666    let (mut components, explicitly_global) =
9667        structured_cpp_qualified_components(declarator_name, source)?;
9668
9669    let terminal = components.pop()?;
9670    let owner_start = components
9671        .iter()
9672        .position(|component| component.is_template_id)?;
9673    let explicit_package = components[..owner_start]
9674        .iter()
9675        .map(|component| component.name.as_str())
9676        .collect::<Vec<_>>()
9677        .join("::");
9678    let explicit_package_is_empty = explicit_package.is_empty();
9679    let package_name = match (
9680        explicitly_global,
9681        scope.package_name.is_empty(),
9682        explicit_package_is_empty,
9683    ) {
9684        (true, _, _) => explicit_package,
9685        (false, _, true) => scope.package_name.clone(),
9686        (false, true, false) => explicit_package,
9687        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9688    };
9689    // Same identity-split fallback as `split_cpp_name` (#1093): a template
9690    // specialization's owner class named with no namespace segment of its own
9691    // (`explicit_package` empty) at file scope (`explicitly_global` false)
9692    // with nothing enclosing (`package_name` still empty) has no structural
9693    // signal for its namespace besides an in-scope `using namespace X;`.
9694    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9695    {
9696        cpp_using_directive_namespace_for_bare_owner(scope)
9697    } else {
9698        package_name
9699    };
9700    let owner_chain = components[owner_start..]
9701        .iter()
9702        .map(|component| component.name.clone())
9703        .collect::<Vec<_>>();
9704    if owner_chain.is_empty() || terminal.name.is_empty() {
9705        return None;
9706    }
9707
9708    Some((
9709        Some(CppMemberOwner::Chain(owner_chain)),
9710        terminal.name,
9711        package_name,
9712    ))
9713}
9714
9715fn canonical_cpp_qualified_component(
9716    mut component: Node<'_>,
9717    source: &str,
9718) -> Option<CppQualifiedNameComponent> {
9719    let mut is_template_id = false;
9720    loop {
9721        match component.kind() {
9722            "template_type" => {
9723                is_template_id = true;
9724                component = component.child_by_field_name("name")?;
9725            }
9726            "dependent_name" => component = component.named_child(0)?,
9727            "identifier"
9728            | "field_identifier"
9729            | "namespace_identifier"
9730            | "type_identifier"
9731            | "operator_name"
9732            | "destructor_name" => {
9733                let name = normalize_cpp_whitespace(node_text(component, source));
9734                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9735                    name,
9736                    is_template_id,
9737                });
9738            }
9739            _ => component = component.child_by_field_name("name")?,
9740        }
9741    }
9742}
9743
9744fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9745    if let Some(name) = macro_decorated_unqualified_name(node) {
9746        return extract_declarator_name(name, source);
9747    }
9748    match node.kind() {
9749        "identifier"
9750        | "field_identifier"
9751        | "type_identifier"
9752        | "operator_name"
9753        | "destructor_name"
9754        | "qualified_identifier" => node_text(node, source).to_string(),
9755        "function_declarator"
9756        | "pointer_declarator"
9757        | "reference_declarator"
9758        | "parenthesized_declarator"
9759        | "array_declarator"
9760        | "template_function" => node
9761            .child_by_field_name("declarator")
9762            .or_else(|| node.child_by_field_name("name"))
9763            .or_else(|| last_named_child(node))
9764            .map(|child| extract_declarator_name(child, source))
9765            .unwrap_or_else(|| node_text(node, source).to_string()),
9766        _ => node
9767            .child_by_field_name("name")
9768            .map(|child| extract_declarator_name(child, source))
9769            .unwrap_or_else(|| node_text(node, source).to_string()),
9770    }
9771}
9772
9773/// Extract a callable identity only through declaration-shaped AST nodes.
9774/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
9775/// expose the call's parameter list as a false function declarator; accepting
9776/// arbitrary node text there emitted bogus names such as `.*f`.
9777fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
9778    if let Some(name) = macro_decorated_unqualified_name(node) {
9779        return extract_callable_declarator_name(name, source);
9780    }
9781    match node.kind() {
9782        "identifier"
9783        | "field_identifier"
9784        | "type_identifier"
9785        | "operator_name"
9786        | "destructor_name"
9787        | "qualified_identifier" => Some(node_text(node, source).to_string()),
9788        "function_declarator"
9789        | "pointer_declarator"
9790        | "reference_declarator"
9791        | "parenthesized_declarator"
9792        | "array_declarator"
9793        | "template_function" => node
9794            .child_by_field_name("declarator")
9795            .or_else(|| node.child_by_field_name("name"))
9796            .and_then(|child| extract_callable_declarator_name(child, source)),
9797        _ => None,
9798    }
9799}
9800
9801fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
9802    match node.kind() {
9803        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
9804            let name = node_text(node, source).trim().to_string();
9805            (!name.is_empty()).then_some(name)
9806        }
9807        _ => node
9808            .child_by_field_name("declarator")
9809            .or_else(|| node.child_by_field_name("name"))
9810            .or_else(|| last_named_child(node))
9811            .and_then(|child| extract_variable_name(child, source)),
9812    }
9813}
9814
9815/// Recover a C field whose name is wrapped in the function-like
9816/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
9817/// function declarator, while retaining its argument as a structured child.
9818/// The exact macro name and one-argument shape keep ordinary function-pointer
9819/// fields and unknown malformed declarators fail-closed.
9820#[derive(Clone, Copy)]
9821pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
9822    pub(crate) name: Node<'tree>,
9823    pub(crate) declarator: Node<'tree>,
9824}
9825
9826impl RecoveredFunctionLikeFieldDeclarator<'_> {
9827    pub(crate) fn pointer_depth(self) -> i32 {
9828        let mut depth = 0;
9829        let mut current = self.declarator;
9830        while current.kind() != "function_declarator" {
9831            if current.kind() == "pointer_declarator" {
9832                depth += 1;
9833            }
9834            current = current
9835                .child_by_field_name("declarator")
9836                .expect("recovered field wrapper has an inner declarator");
9837        }
9838        depth
9839    }
9840}
9841
9842pub(crate) fn recovered_function_like_field_declarator<'tree>(
9843    node: Node<'tree>,
9844    source: &str,
9845) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
9846    if node.kind() != "field_declaration" {
9847        return None;
9848    }
9849    let outer_declarator = node.child_by_field_name("declarator")?;
9850    let mut declarator = outer_declarator;
9851    while matches!(
9852        declarator.kind(),
9853        "pointer_declarator"
9854            | "reference_declarator"
9855            | "array_declarator"
9856            | "parenthesized_declarator"
9857    ) {
9858        declarator = declarator.child_by_field_name("declarator")?;
9859    }
9860    if declarator.kind() != "function_declarator" {
9861        return None;
9862    }
9863    let macro_name = declarator.child_by_field_name("declarator")?;
9864    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
9865    {
9866        return None;
9867    }
9868    let parameters = declarator.child_by_field_name("parameters")?;
9869    let mut cursor = parameters.walk();
9870    let mut arguments = parameters.named_children(&mut cursor);
9871    let parameter = arguments.next()?;
9872    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
9873        return None;
9874    }
9875    let name = parameter.child_by_field_name("type").filter(|argument| {
9876        matches!(
9877            argument.kind(),
9878            "identifier" | "field_identifier" | "type_identifier"
9879        )
9880    })?;
9881    Some(RecoveredFunctionLikeFieldDeclarator {
9882        name,
9883        declarator: outer_declarator,
9884    })
9885}
9886
9887fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
9888    let count = node.named_child_count();
9889    if count == 0 {
9890        None
9891    } else {
9892        node.named_child(count - 1)
9893    }
9894}
9895
9896fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
9897    let name_node = node.child_by_field_name("name")?;
9898    let name = normalize_cpp_whitespace(node_text(name_node, source));
9899    (!name.is_empty()).then_some(name)
9900}
9901
9902fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9903    if node.kind() != "declaration" {
9904        return Vec::new();
9905    }
9906    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
9907        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
9908    }) else {
9909        return Vec::new();
9910    };
9911    let Some(declarator) = node.child_by_field_name("declarator") else {
9912        return Vec::new();
9913    };
9914    if node_text(keyword, source) == "using"
9915        && (declarator.kind() != "init_declarator"
9916            || declarator.child_by_field_name("value").is_none())
9917    {
9918        return Vec::new();
9919    }
9920    if node_text(keyword, source) == "typedef"
9921        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
9922    {
9923        return vec![alias_name];
9924    }
9925    extract_typedef_declarator_name(declarator, source)
9926        .into_iter()
9927        .collect()
9928}
9929
9930fn recovered_typedef_error_alias_name(
9931    declaration: Node<'_>,
9932    declarator: Node<'_>,
9933    source: &str,
9934) -> Option<String> {
9935    // An export macro between `class` and its name can make tree-sitter parse
9936    // the recovered class body as a function body. In that shape,
9937    //
9938    //     typedef spi::Filter BASE_CLASS;
9939    //
9940    // becomes a declaration whose `declarator` is the underlying qualified
9941    // type (`spi::Filter`) and whose actual alias name is displaced into the
9942    // following ERROR node. Do not publish the terminal underlying type
9943    // (`Filter`) as a false class-owned alias.
9944    if declarator.kind() != "qualified_identifier" {
9945        return None;
9946    }
9947    let mut cursor = declaration.walk();
9948    let mut errors = declaration
9949        .named_children(&mut cursor)
9950        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
9951    let error = errors.next()?;
9952    if errors.next().is_some() || error.named_child_count() != 1 {
9953        return None;
9954    }
9955    let name = error.named_child(0)?;
9956    if !matches!(
9957        name.kind(),
9958        "identifier" | "field_identifier" | "type_identifier"
9959    ) {
9960        return None;
9961    }
9962    let name = normalize_cpp_whitespace(node_text(name, source));
9963    (!name.is_empty()).then_some(name)
9964}
9965
9966fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
9967    // A function-like token in the type position can make tree-sitter expose
9968    // its argument as a parenthesized declarator. Do not publish that argument
9969    // as an alias. The macro-specific recovery below handles the proven shape.
9970    if fragmented_parenthesized_typedef_type(node).is_some() {
9971        return Vec::new();
9972    }
9973    let has_function_like_macro_type = node
9974        .child_by_field_name("type")
9975        .filter(|type_node| type_node.kind() == "type_identifier")
9976        .is_some_and(|type_node| {
9977            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
9978        });
9979    let mut names = Vec::new();
9980    let mut cursor = node.walk();
9981    for declarator in node.children_by_field_name("declarator", &mut cursor) {
9982        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
9983            continue;
9984        }
9985        if let Some(name) = extract_typedef_declarator_name(declarator, source)
9986            && !names.contains(&name)
9987        {
9988            names.push(name);
9989        }
9990    }
9991    names
9992}
9993
9994struct RecoveredMacroTypedefAlias<'tree> {
9995    name: String,
9996    end_node: Node<'tree>,
9997}
9998
9999/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
10000/// into an identifier expression statement. The uppercase macro token, missing
10001/// typedef terminator, and complete sibling terminator prove this exact shape.
10002fn recovered_macro_typedef_alias<'tree>(
10003    node: Node<'tree>,
10004    source: &str,
10005) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10006    let type_node = fragmented_parenthesized_typedef_type(node)?;
10007    if type_node.kind() != "type_identifier"
10008        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10009    {
10010        return None;
10011    }
10012
10013    let end_node = node.next_named_sibling()?;
10014    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10015        return None;
10016    }
10017    let name_node = end_node.named_child(0)?;
10018    if name_node.kind() != "identifier" {
10019        return None;
10020    }
10021    let has_terminator = (0..end_node.child_count()).any(|index| {
10022        end_node
10023            .child(index)
10024            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10025    });
10026    if !has_terminator {
10027        return None;
10028    }
10029    let name = normalize_cpp_whitespace(node_text(name_node, source));
10030    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10031}
10032
10033fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10034    if node.kind() != "type_definition" {
10035        return None;
10036    }
10037    let mut declarator_cursor = node.walk();
10038    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10039    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10040        return None;
10041    }
10042    let has_missing_terminator = (0..node.child_count()).any(|index| {
10043        node.child(index)
10044            .is_some_and(|child| child.kind() == ";" && child.is_missing())
10045    });
10046    if !has_missing_terminator {
10047        return None;
10048    }
10049    node.child_by_field_name("type")
10050}
10051
10052fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10053    match node.kind() {
10054        "identifier" | "field_identifier" | "type_identifier" => {
10055            let name = normalize_cpp_whitespace(node_text(node, source));
10056            (!name.is_empty()).then_some(name)
10057        }
10058        "qualified_identifier" => node
10059            .child_by_field_name("name")
10060            .and_then(|name| extract_typedef_declarator_name(name, source)),
10061        _ => node
10062            .child_by_field_name("declarator")
10063            .or_else(|| node.child_by_field_name("name"))
10064            .or_else(|| last_named_child(node))
10065            .and_then(|child| extract_typedef_declarator_name(child, source)),
10066    }
10067}
10068
10069fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10070    let name = node
10071        .child_by_field_name("name")
10072        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10073        .or_else(|| {
10074            let mut cursor = node.walk();
10075            node.named_children(&mut cursor)
10076                .find(|child| {
10077                    matches!(
10078                        child.kind(),
10079                        "identifier" | "field_identifier" | "type_identifier"
10080                    )
10081                })
10082                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10083        })?;
10084    (!name.is_empty()).then_some(name)
10085}
10086
10087fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10088    left.id() == right.id()
10089}
10090
10091fn render_cpp_type_signature(
10092    node: Node<'_>,
10093    source: &str,
10094    template_signature: Option<&str>,
10095) -> String {
10096    let text = normalize_cpp_whitespace(node_text(node, source));
10097    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10098    let rendered = if head.ends_with(';') {
10099        head.to_string()
10100    } else {
10101        format!("{head} {{")
10102    };
10103    if let Some(template_signature) = template_signature {
10104        format!("template {template_signature} {rendered}")
10105    } else {
10106        rendered
10107    }
10108}
10109
10110fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10111    if let Some(recovered) = recovered_pyobject_head_field(node, source)
10112        && recovered.declarator == declarator
10113    {
10114        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10115        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10116        return format!("{type_text} {declarator};");
10117    }
10118    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10119        && recovered.name == declarator
10120    {
10121        let type_text = node
10122            .child_by_field_name("type")
10123            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10124            .unwrap_or_default();
10125        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10126        let mut prefix = String::new();
10127        let mut suffix = String::new();
10128        let mut current = recovered.declarator;
10129        while current.kind() != "function_declarator" {
10130            match current.kind() {
10131                "pointer_declarator" => prefix.push('*'),
10132                "reference_declarator" => prefix.push('&'),
10133                "array_declarator" => {
10134                    let size = current
10135                        .child_by_field_name("size")
10136                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10137                        .unwrap_or_default();
10138                    suffix.push('[');
10139                    suffix.push_str(&size);
10140                    suffix.push(']');
10141                }
10142                "parenthesized_declarator" => {}
10143                _ => unreachable!("validated recovered field declarator wrapper"),
10144            }
10145            current = current
10146                .child_by_field_name("declarator")
10147                .expect("recovered field wrapper has an inner declarator");
10148        }
10149        let separator = if prefix.is_empty() { "" } else { " " };
10150        return format!("{type_text} {prefix}{separator}{name}{suffix};");
10151    }
10152    if let Some(signature) =
10153        render_recovered_macro_qualified_field_signature(node, declarator, source)
10154    {
10155        return signature;
10156    }
10157    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10158    let prefix = cpp_declaration_prefix(node, source);
10159    let name = extract_variable_name(declarator, source).unwrap_or_default();
10160    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10161    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10162        || (prefix.ends_with('&') && raw_suffix == "&")
10163    {
10164        String::new()
10165    } else {
10166        raw_suffix
10167    };
10168
10169    let mut rendered = if suffix.is_empty() {
10170        format!("{prefix} {name}")
10171    } else if suffix.starts_with('*') || suffix.starts_with('&') {
10172        format!("{prefix}{suffix} {name}")
10173    } else if suffix.starts_with('[') || suffix.starts_with('(') {
10174        format!("{prefix} {name}{suffix}")
10175    } else {
10176        format!("{prefix} {suffix}{name}")
10177    };
10178    rendered = collapse_cpp_whitespace(&rendered);
10179
10180    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10181        format!("{rendered} = {initializer};")
10182    } else if declaration_text.ends_with(';') {
10183        format!("{rendered};")
10184    } else {
10185        rendered
10186    }
10187}
10188
10189fn render_recovered_macro_qualified_field_signature(
10190    node: Node<'_>,
10191    declarator: Node<'_>,
10192    source: &str,
10193) -> Option<String> {
10194    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10195    if !recovered
10196        .iter()
10197        .any(|candidate| same_node(*candidate, declarator))
10198    {
10199        return None;
10200    }
10201    let pseudo_declarator = node.child_by_field_name("declarator")?;
10202    let mut cursor = node.walk();
10203    let clause = node
10204        .named_children(&mut cursor)
10205        .find(|child| child.kind() == "bitfield_clause")?;
10206    let mut cursor = clause.walk();
10207    let error = clause
10208        .named_children(&mut cursor)
10209        .find(|child| child.kind() == "ERROR")?;
10210    let qualified_type =
10211        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10212    let prefix = cpp_declaration_prefix(node, source);
10213    let name = extract_variable_name(declarator, source)?;
10214    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10215    let mut rendered = if suffix.is_empty() {
10216        format!("{prefix} {qualified_type} {name}")
10217    } else {
10218        format!("{prefix} {qualified_type} {suffix} {name}")
10219    };
10220    rendered = collapse_cpp_whitespace(&rendered);
10221
10222    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10223        Some(format!(
10224            "{rendered} = {};",
10225            normalize_cpp_whitespace(node_text(initializer, source))
10226        ))
10227    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10228        Some(format!("{rendered} = {initializer};"))
10229    } else {
10230        Some(format!("{rendered};"))
10231    }
10232}
10233
10234fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10235    match node.kind() {
10236        "pointer_expression" => {
10237            let operator = node
10238                .child_by_field_name("operator")
10239                .or_else(|| node.child(0))
10240                .map(|operator| node_text(operator, source))
10241                .unwrap_or("*");
10242            let argument = node
10243                .child_by_field_name("argument")
10244                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10245                .unwrap_or_default();
10246            format!("{operator}{argument}")
10247        }
10248        "unary_expression" => {
10249            let operator = node
10250                .child_by_field_name("operator")
10251                .or_else(|| node.child(0))
10252                .map(|operator| node_text(operator, source))
10253                .unwrap_or_default();
10254            let argument = node
10255                .child_by_field_name("argument")
10256                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10257                .unwrap_or_default();
10258            format!("{operator}{argument}")
10259        }
10260        "identifier" | "field_identifier" => String::new(),
10261        _ => cpp_declarator_suffix_without_name(node, source),
10262    }
10263}
10264
10265fn recovered_macro_qualified_field_initializer<'tree>(
10266    clause: Node<'tree>,
10267    declarator: Node<'tree>,
10268) -> Option<Node<'tree>> {
10269    let mut stack = vec![clause];
10270    while let Some(current) = stack.pop() {
10271        if current.kind() == "assignment_expression"
10272            && current
10273                .child_by_field_name("left")
10274                .is_some_and(|left| same_node(left, declarator))
10275        {
10276            return current.child_by_field_name("right");
10277        }
10278        let mut cursor = current.walk();
10279        stack.extend(current.named_children(&mut cursor));
10280    }
10281    None
10282}
10283
10284fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10285    let text = node_text(node, source);
10286    let mut cursor = node.walk();
10287    let first_declarator = node.named_children(&mut cursor).find(|child| {
10288        matches!(
10289            child.kind(),
10290            "init_declarator"
10291                | "identifier"
10292                | "field_identifier"
10293                | "pointer_declarator"
10294                | "reference_declarator"
10295                | "array_declarator"
10296                | "function_declarator"
10297        )
10298    });
10299    let prefix = if let Some(first_declarator) = first_declarator {
10300        let end = first_declarator
10301            .start_byte()
10302            .saturating_sub(node.start_byte());
10303        let mut prefix = text.get(..end).unwrap_or(text).to_string();
10304        let declarator_suffix = match first_declarator.kind() {
10305            "init_declarator" => first_declarator
10306                .child_by_field_name("declarator")
10307                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10308                .unwrap_or_default(),
10309            _ => cpp_declarator_suffix_without_name(first_declarator, source),
10310        };
10311        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10312            prefix.push_str(&declarator_suffix);
10313        }
10314        return collapse_cpp_whitespace(&prefix)
10315            .trim_end_matches(',')
10316            .trim_end_matches(';')
10317            .trim()
10318            .to_string();
10319    } else {
10320        text
10321    };
10322    collapse_cpp_whitespace(prefix)
10323        .trim_end_matches(',')
10324        .trim_end_matches(';')
10325        .trim()
10326        .to_string()
10327}
10328
10329fn cpp_preserved_initializer(
10330    declaration_node: Node<'_>,
10331    declarator: Node<'_>,
10332    source: &str,
10333) -> Option<String> {
10334    let name = extract_variable_name(declarator, source)?;
10335    let mut cursor = declaration_node.walk();
10336    for child in declaration_node.named_children(&mut cursor) {
10337        if child.kind() != "init_declarator" {
10338            continue;
10339        }
10340        let Some(inner) = child.child_by_field_name("declarator") else {
10341            continue;
10342        };
10343        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10344            continue;
10345        }
10346        let value = child.child_by_field_name("value")?;
10347        let kind = value.kind();
10348        if matches!(
10349            kind,
10350            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10351        ) {
10352            return Some(normalize_cpp_whitespace(node_text(value, source)));
10353        }
10354        break;
10355    }
10356    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10357    let pattern = format!(
10358        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10359        regex::escape(&name)
10360    );
10361    Regex::new(&pattern)
10362        .ok()
10363        .and_then(|regex| regex.captures(&declaration_text))
10364        .and_then(|captures| captures.get(1))
10365        .map(|value| value.as_str().to_string())
10366}
10367
10368fn render_cpp_function_display_signature_from_node<'tree>(
10369    node: Node<'tree>,
10370    source: &str,
10371    template_signature: Option<&str>,
10372    has_body: bool,
10373    ancestry: &ParentIndex<'tree>,
10374) -> String {
10375    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10376    let parent_text = node_text(root, source);
10377    let body_local_start = root
10378        .child_by_field_name("body")
10379        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10380        .unwrap_or(parent_text.len());
10381    let display = parent_text
10382        .get(..body_local_start)
10383        .unwrap_or(parent_text)
10384        .trim()
10385        .trim();
10386    let display = if let Some(template_signature) = template_signature {
10387        if display.starts_with("template ") {
10388            display.to_string()
10389        } else {
10390            format!("template {template_signature} {display}")
10391        }
10392    } else {
10393        display.to_string()
10394    };
10395    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10396    if has_body {
10397        format!("{display} {{...}}")
10398    } else {
10399        format!("{display};")
10400    }
10401}
10402
10403fn cpp_template_signature(
10404    template_node: Node<'_>,
10405    declaration_child: Node<'_>,
10406    source: &str,
10407) -> Option<String> {
10408    let text = source
10409        .get(template_node.start_byte()..declaration_child.start_byte())
10410        .unwrap_or("");
10411    let text = normalize_cpp_whitespace(text);
10412    let start = text.find('<')?;
10413    let end = text.rfind('>')?;
10414    if end < start {
10415        return None;
10416    }
10417    Some(text[start..=end].to_string())
10418}
10419
10420struct RecoveredFragmentedPartialSpecialization<'tree> {
10421    declaration_node: Node<'tree>,
10422    name: String,
10423    range: Range,
10424    prefix_members: Vec<Node<'tree>>,
10425    member_siblings: Vec<Node<'tree>>,
10426    following_declarations: Vec<Node<'tree>>,
10427}
10428
10429struct RecoveredFragmentedPreprocessorClass<'tree> {
10430    declaration_node: Node<'tree>,
10431    class_node: Node<'tree>,
10432    body: Node<'tree>,
10433    name: String,
10434    range: Range,
10435    tail_members: Vec<Node<'tree>>,
10436    member_siblings: Vec<Node<'tree>>,
10437}
10438
10439/// Recover a class whose preprocessor-fragmented parse closes at an early
10440/// member body and publishes the remaining in-class declarations as siblings
10441/// of the surrounding alternative. Primary classes are admitted only when an
10442/// earlier branch contains the matching bodyless declaration and the class
10443/// node retains the displaced `#endif`. Partial specializations instead carry
10444/// their identity structurally in the `template_type` name and template
10445/// metadata. Retain the original AST nodes and re-own only the siblings through
10446/// the displaced structural `};` terminator.
10447fn recover_fragmented_preprocessor_class<'tree>(
10448    template_node: Node<'tree>,
10449    source: &str,
10450    ancestry: &ParentIndex<'tree>,
10451) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10452    let alternative = ancestry.parent(template_node)?;
10453    if alternative.kind() != "preproc_else" {
10454        return None;
10455    }
10456    let conditional = alternative.parent()?;
10457    if conditional.kind() != "preproc_if" {
10458        return None;
10459    }
10460    let declaration_node = template_node
10461        .named_children(&mut template_node.walk())
10462        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10463    let class_node = declaration_node
10464        .named_children(&mut declaration_node.walk())
10465        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10466    let body = cpp_body_node(class_node)?;
10467    if class_node.end_byte() >= declaration_node.end_byte() {
10468        return None;
10469    }
10470    let name = class_like_name(class_node, source, ancestry)?;
10471    let is_partial_specialization = class_node
10472        .child_by_field_name("name")
10473        .is_some_and(|class_name| class_name.kind() == "template_type");
10474    if is_partial_specialization {
10475        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10476        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10477            return None;
10478        }
10479    } else {
10480        if !class_has_displaced_preprocessor_terminator(class_node) {
10481            return None;
10482        }
10483        let matching_other_branch = conditional
10484            .named_children(&mut conditional.walk())
10485            .take_while(|child| !same_node(*child, alternative))
10486            .filter(|child| child.kind() == "template_declaration")
10487            .filter_map(first_class_like_child)
10488            .any(|candidate| {
10489                cpp_body_node(candidate).is_none()
10490                    && class_like_name(candidate, source, ancestry).as_deref()
10491                        == Some(name.as_str())
10492            });
10493        if !matching_other_branch {
10494            return None;
10495        }
10496    }
10497
10498    let mut tail_members = Vec::new();
10499    let mut saw_class = false;
10500    let mut declaration_cursor = declaration_node.walk();
10501    for child in declaration_node.named_children(&mut declaration_cursor) {
10502        if same_node(child, class_node) {
10503            saw_class = true;
10504        } else if saw_class {
10505            tail_members.push(child);
10506        }
10507    }
10508
10509    let mut member_siblings = Vec::new();
10510    let mut saw_template = false;
10511    let mut terminator = None;
10512    for index in 0..alternative.child_count() {
10513        let Some(child) = alternative.child(index) else {
10514            continue;
10515        };
10516        if same_node(child, template_node) {
10517            saw_template = true;
10518            continue;
10519        }
10520        if !saw_template {
10521            continue;
10522        }
10523        if displaced_fragmented_class_terminator(alternative, index) {
10524            terminator = alternative.child(index + 1);
10525            break;
10526        }
10527        if child.is_named() {
10528            member_siblings.push(child);
10529        }
10530    }
10531    let terminator = terminator?;
10532    Some(RecoveredFragmentedPreprocessorClass {
10533        declaration_node,
10534        class_node,
10535        body,
10536        name,
10537        range: Range {
10538            start_byte: class_node.start_byte(),
10539            end_byte: terminator.end_byte(),
10540            start_line: class_node.start_position().row + 1,
10541            end_line: terminator.end_position().row + 1,
10542        },
10543        tail_members,
10544        member_siblings,
10545    })
10546}
10547
10548fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10549    (0..class_node.child_count()).any(|index| {
10550        class_node.child(index).is_some_and(|child| {
10551            child.kind() == "ERROR"
10552                && (0..child.child_count()).any(|error_index| {
10553                    child
10554                        .child(error_index)
10555                        .is_some_and(|token| token.kind() == "#endif")
10556                })
10557        })
10558    })
10559}
10560
10561/// The real `#endif` that tree-sitter consumed inside an error subtree.
10562///
10563/// A preprocessor directive inside a malformed array bound can cause later
10564/// declarations to remain children of the conditional. The non-missing token
10565/// still gives the exact structured boundary. Ignore nested conditionals and
10566/// select the last unpaired error-owned token. Tree-sitter can pair a later
10567/// outer `#endif` with this conditional, so the direct terminator is not
10568/// necessarily missing.
10569pub fn cpp_displaced_preprocessor_terminator<'tree>(
10570    conditional: Node<'tree>,
10571) -> Option<Node<'tree>> {
10572    if !conditional.has_error() {
10573        return None;
10574    }
10575    let has_concrete_direct_terminator = conditional
10576        .child_count()
10577        .checked_sub(1)
10578        .and_then(|index| conditional.child(index))
10579        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10580    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10581        // A structured alternative proves that the direct `#endif` closes
10582        // this family. An error-owned terminator inside either branch belongs
10583        // to a damaged nested conditional, not to this one.
10584        return None;
10585    }
10586    // Pair concrete directive tokens while walking the recovered subtree. An
10587    // inner conditional may be represented only by its `#ifndef`/`#endif`
10588    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
10589    // sufficient: an inner `#endif` can otherwise be mistaken for this
10590    // conditional's displaced terminator.
10591    let mut displaced = None;
10592    let mut conditional_depth = 0usize;
10593    let mut stack = children_iter(conditional)
10594        .map(|child| (child, false))
10595        .collect::<Vec<_>>();
10596    stack.reverse();
10597    while let Some((node, inside_error)) = stack.pop() {
10598        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10599            continue;
10600        }
10601        if node != conditional
10602            && matches!(
10603                node.kind(),
10604                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10605            )
10606        {
10607            continue;
10608        }
10609        let inside_error = inside_error || node.kind() == "ERROR";
10610        match node.kind() {
10611            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10612                conditional_depth += 1;
10613            }
10614            "#endif" if !node.is_missing() => {
10615                if conditional_depth == 0
10616                    && inside_error
10617                    && displaced
10618                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10619                {
10620                    displaced = Some(node);
10621                }
10622                conditional_depth = conditional_depth.saturating_sub(1);
10623            }
10624            _ => {}
10625        }
10626        let first_pushed = stack.len();
10627        stack.extend(children_iter(node).map(|child| (child, inside_error)));
10628        stack[first_pushed..].reverse();
10629    }
10630    displaced
10631}
10632
10633/// The effective end of a conditional whose real terminator tree-sitter
10634/// displaced into declaration recovery.
10635///
10636/// Most damaged conditionals retain a concrete `#endif` token below an
10637/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
10638/// boundary. A preprocessor family that selects the middle of a declaration
10639/// can lose the directive tokens entirely. In that shape tree-sitter leaves
10640/// the declaration's `typedef` token as the sole child of the immediately
10641/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
10642/// declarator name inside the conditional's first declaration. The declaration
10643/// end is then the smallest structured boundary that contains the whole split
10644/// declaration.
10645#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10646pub struct CppDisplacedPreprocessorBoundary {
10647    pub end_byte: usize,
10648    pub end_line: usize,
10649}
10650
10651pub fn cpp_displaced_preprocessor_boundary(
10652    conditional: Node<'_>,
10653) -> Option<CppDisplacedPreprocessorBoundary> {
10654    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10655        return Some(CppDisplacedPreprocessorBoundary {
10656            end_byte: terminator.end_byte(),
10657            end_line: terminator.end_position().row + 1,
10658        });
10659    }
10660    if let Some(declaration) = displaced_split_declaration(conditional) {
10661        return Some(CppDisplacedPreprocessorBoundary {
10662            end_byte: declaration.end_byte(),
10663            end_line: declaration.end_position().row + 1,
10664        });
10665    }
10666    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10667        return Some(CppDisplacedPreprocessorBoundary {
10668            end_byte: terminator.end_byte(),
10669            end_line: terminator.end_position().row + 1,
10670        });
10671    }
10672    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10673        return Some(CppDisplacedPreprocessorBoundary {
10674            end_byte: terminator.end_byte(),
10675            end_line: terminator.end_position().row + 1,
10676        });
10677    }
10678    None
10679}
10680
10681/// Recover an outer terminator that tree-sitter assigned to a damaged nested
10682/// conditional. This occurs when a split construct such as `extern "C"`
10683/// consumes the nested `#endif` inside an error node: the nested conditional's
10684/// direct terminator is then the outer conditional's real terminator, while
10685/// the outer node ends with a missing token and absorbs later declarations.
10686fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10687    if !conditional.has_error()
10688        || conditional.child_by_field_name("alternative").is_some()
10689        || conditional
10690            .child(conditional.child_count().saturating_sub(1))
10691            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10692    {
10693        return None;
10694    }
10695    let mut recovered = None;
10696    for nested in named_children_iter(conditional) {
10697        if !matches!(
10698            nested.kind(),
10699            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10700        ) || nested.child_by_field_name("alternative").is_some()
10701        {
10702            continue;
10703        }
10704        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10705            continue;
10706        };
10707        if direct.kind() != "#endif" || direct.is_missing() {
10708            continue;
10709        }
10710        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10711            continue;
10712        };
10713        if displaced.end_byte() >= direct.start_byte() {
10714            continue;
10715        }
10716        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10717            recovered = Some(direct);
10718        }
10719    }
10720    recovered
10721}
10722
10723fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10724    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10725        return None;
10726    }
10727    let mut cursor = conditional.walk();
10728    let declarations = conditional
10729        .named_children(&mut cursor)
10730        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
10731        .collect::<Vec<_>>();
10732    let declaration = *declarations.first()?;
10733    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
10734        return None;
10735    }
10736    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
10737    let mut terminator = None;
10738    let mut stack = (0..declaration.child_count())
10739        .filter_map(|index| declaration.child(index))
10740        .filter(|child| child.start_byte() < declarator_start)
10741        .map(|child| (child, false))
10742        .collect::<Vec<_>>();
10743    while let Some((node, inside_error)) = stack.pop() {
10744        let inside_error = inside_error || node.kind() == "ERROR";
10745        if inside_error && node.kind() == "#endif" && !node.is_missing() {
10746            terminator = Some(node);
10747            continue;
10748        }
10749        for index in 0..node.child_count() {
10750            if let Some(child) = node.child(index)
10751                && child.start_byte() < declarator_start
10752            {
10753                stack.push((child, inside_error));
10754            }
10755        }
10756    }
10757    terminator
10758}
10759
10760fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10761    if !conditional.has_error()
10762        || conditional.child_by_field_name("alternative").is_some()
10763        || conditional
10764            .prev_named_sibling()
10765            .filter(|sibling| {
10766                sibling.kind() == "ERROR"
10767                    && sibling.child_count() == 1
10768                    && sibling
10769                        .child(0)
10770                        .is_some_and(|child| child.kind() == "typedef")
10771            })
10772            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
10773            .is_none()
10774    {
10775        return None;
10776    }
10777    let mut cursor = conditional.walk();
10778    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
10779    let declaration_index = children
10780        .iter()
10781        .position(|child| child.kind() == "declaration" && child.has_error())?;
10782    let declaration = children[declaration_index];
10783    if !children
10784        .iter()
10785        .skip(declaration_index + 1)
10786        .any(|child| child.end_byte() > declaration.end_byte())
10787    {
10788        return None;
10789    }
10790    let declarator = declaration.child_by_field_name("declarator")?;
10791    let mut error_end = None;
10792    let mut names = Vec::new();
10793    let mut stack = vec![declarator];
10794    while let Some(node) = stack.pop() {
10795        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
10796            error_end =
10797                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
10798            continue;
10799        }
10800        if matches!(node.kind(), "identifier" | "type_identifier") {
10801            names.push(node.start_byte());
10802        }
10803        push_named_children_reversed(node, &mut stack);
10804    }
10805    let error_end = error_end?;
10806    names
10807        .into_iter()
10808        .any(|start| start >= error_end)
10809        .then_some(declaration)
10810}
10811
10812fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
10813    let Some(error) = parent.child(error_index) else {
10814        return false;
10815    };
10816    if error.kind() != "ERROR"
10817        || error.child_count() != 1
10818        || error.child(0).is_none_or(|child| child.kind() != "}")
10819    {
10820        return false;
10821    }
10822    let Some(semicolon) = parent.child(error_index + 1) else {
10823        return false;
10824    };
10825    semicolon.kind() == "expression_statement"
10826        && semicolon.child_count() == 1
10827        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
10828}
10829
10830/// Locate the real end of a class-like declaration when a macro invocation
10831/// without a source semicolon absorbs the class's `};` into its parsed field.
10832/// The grammar then keeps following namespace declarations as later children
10833/// of the same field list. The direct ERROR-plus-semicolon pair proves the
10834/// boundary structurally; no source-text delimiter scan is needed.
10835fn displaced_macro_class_tail(
10836    declaration_node: Node<'_>,
10837    body: Node<'_>,
10838    source: &str,
10839) -> Option<DisplacedMacroClassTail> {
10840    if !matches!(
10841        declaration_node.kind(),
10842        "class_specifier" | "struct_specifier" | "union_specifier"
10843    ) || body.kind() != "field_declaration_list"
10844    {
10845        return None;
10846    }
10847
10848    let child_count = body.named_child_count();
10849    for index in 0..child_count {
10850        let child = body.named_child(index)?;
10851        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
10852            continue;
10853        };
10854        let split_index = index + 1;
10855        if split_index >= child_count {
10856            return None;
10857        }
10858        let mut cursor = body.walk();
10859        if !body
10860            .named_children(&mut cursor)
10861            .skip(split_index)
10862            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
10863        {
10864            return None;
10865        }
10866        return Some(DisplacedMacroClassTail {
10867            split_index,
10868            class_range: Range {
10869                start_byte: declaration_node.start_byte(),
10870                end_byte: terminator.end_byte(),
10871                start_line: declaration_node.start_position().row + 1,
10872                end_line: terminator.end_position().row + 1,
10873            },
10874        });
10875    }
10876    None
10877}
10878
10879fn displaced_macro_field_terminator<'tree>(
10880    field: Node<'tree>,
10881    source: &str,
10882) -> Option<Node<'tree>> {
10883    if field.kind() != "field_declaration" {
10884        return None;
10885    }
10886    let macro_type = field.child_by_field_name("type")?;
10887    if macro_type.kind() != "type_identifier"
10888        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10889        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
10890    {
10891        return None;
10892    }
10893    for index in 0..field.child_count() {
10894        let error = field.child(index)?;
10895        if error.kind() != "ERROR"
10896            || error.child_count() != 1
10897            || error.child(0).is_none_or(|child| child.kind() != "}")
10898        {
10899            continue;
10900        }
10901        let semicolon = field.child(index + 1)?;
10902        if semicolon.kind() == ";" {
10903            return Some(semicolon);
10904        }
10905    }
10906    None
10907}
10908
10909fn recover_fragmented_partial_specialization<'tree>(
10910    template_node: Node<'tree>,
10911    declaration_child: Node<'tree>,
10912    source: &str,
10913    ancestry: &ParentIndex<'tree>,
10914) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
10915    if declaration_child.kind() != "function_definition" {
10916        return None;
10917    }
10918    let class_node = declaration_child.child_by_field_name("type")?;
10919    if !matches!(
10920        class_node.kind(),
10921        "class_specifier" | "struct_specifier" | "union_specifier"
10922    ) || !class_node
10923        .child_by_field_name("name")
10924        .and_then(|name| direct_identifier_name(name, source))
10925        .is_some_and(|name| cpp_export_macro_token(&name))
10926    {
10927        return None;
10928    }
10929    let declarator = declaration_child.child_by_field_name("declarator")?;
10930    if declarator.kind() != "template_function" {
10931        return None;
10932    }
10933    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
10934    if metadata.specialization_arguments.is_empty() {
10935        return None;
10936    }
10937    let body = declaration_child.child_by_field_name("body")?;
10938    if body.kind() != "compound_statement" {
10939        return None;
10940    }
10941    let complete_prefix = body.named_child(0).filter(|first| {
10942        first.kind() == "labeled_statement"
10943            && first.has_error()
10944            && first
10945                .named_child(first.named_child_count().saturating_sub(1))
10946                .is_some_and(recovered_declaration_has_class_terminator)
10947    });
10948    let complete_body = complete_prefix.is_some();
10949    let mut prefix_members = Vec::new();
10950    if let Some(prefix) = complete_prefix {
10951        prefix_members.push(prefix);
10952    } else {
10953        let mut body_cursor = body.walk();
10954        for child in body.named_children(&mut body_cursor) {
10955            if !is_structurally_valid_fragmented_class_prefix_member(child) {
10956                break;
10957            }
10958            prefix_members.push(child);
10959        }
10960    }
10961    let containing_declarations = template_node.parent()?;
10962    if !matches!(
10963        containing_declarations.kind(),
10964        "declaration_list" | "compound_statement"
10965    ) {
10966        return None;
10967    }
10968    let mut member_siblings = Vec::new();
10969    let mut following_declarations = Vec::new();
10970    let terminator;
10971    if complete_body {
10972        terminator = complete_prefix?;
10973        let mut cursor = body.walk();
10974        let mut after_prefix = false;
10975        for child in body.named_children(&mut cursor) {
10976            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
10977                after_prefix = true;
10978            } else if after_prefix {
10979                following_declarations.push(child);
10980            }
10981        }
10982    } else {
10983        let mut found_template = false;
10984        let mut cursor = containing_declarations.walk();
10985        let mut class_terminator = None;
10986        for child in containing_declarations.children(&mut cursor) {
10987            if same_node(child, template_node) {
10988                found_template = true;
10989                continue;
10990            }
10991            if found_template && child.kind() == "}" {
10992                class_terminator = Some(child);
10993                break;
10994            }
10995            // A namespace can never be a class member: reaching one before the
10996            // terminator proves the class's true close was swallowed upstream
10997            // and this scan has crossed into the enclosing scope, so the
10998            // recovery cannot be bounded -- continuing re-owns the namespace
10999            // block (and its template specializations) as class members under
11000            // a re-appended package, desyncing the fq boundary (#2306).
11001            if found_template && child.kind() == "namespace_definition" {
11002                return None;
11003            }
11004            if found_template && child.is_named() {
11005                member_siblings.push(child);
11006            }
11007        }
11008        terminator = class_terminator?;
11009    }
11010    let name = format!(
11011        "{}<{}>",
11012        metadata.primary_name,
11013        metadata
11014            .specialization_arguments
11015            .iter()
11016            .map(|argument| argument.text.as_str())
11017            .collect::<Vec<_>>()
11018            .join(", ")
11019    );
11020    Some(RecoveredFragmentedPartialSpecialization {
11021        declaration_node: declaration_child,
11022        name,
11023        range: Range {
11024            start_byte: declaration_child.start_byte(),
11025            end_byte: terminator.end_byte(),
11026            start_line: declaration_child.start_position().row + 1,
11027            end_line: terminator.end_position().row + 1,
11028        },
11029        prefix_members,
11030        member_siblings,
11031        following_declarations,
11032    })
11033}
11034
11035/// Whether `node` is the function-shaped parser wrapper whose body was
11036/// recovered as a fragmented partial-specialization class body.
11037///
11038/// Consumers that walk lexical function scopes must use the same complete
11039/// structural proof as declaration extraction. Otherwise member callables in
11040/// the recovered body are mistaken for function-local declarations.
11041pub fn is_recovered_fragmented_partial_specialization_container(
11042    node: Node<'_>,
11043    source: &str,
11044) -> bool {
11045    let Some(template) = node
11046        .parent()
11047        .filter(|parent| parent.kind() == "template_declaration")
11048    else {
11049        return false;
11050    };
11051    let mut root = template;
11052    while let Some(parent) = root.parent() {
11053        root = parent;
11054    }
11055    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11056        .is_some()
11057}
11058
11059fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11060    if declaration.kind() != "declaration" {
11061        return false;
11062    }
11063    // With an export macro between `class` and its name, tree-sitter folds a
11064    // complete class body into a function-shaped declaration. The class's own
11065    // `};` remains structurally identifiable as a direct ERROR child holding
11066    // `}`, immediately followed by the declaration's direct `;` child.
11067    (0..declaration.child_count().saturating_sub(1)).any(|index| {
11068        let Some(error) = declaration.child(index) else {
11069            return false;
11070        };
11071        error.kind() == "ERROR"
11072            && error.child_count() == 1
11073            && error.child(0).is_some_and(|child| child.kind() == "}")
11074            && declaration
11075                .child(index + 1)
11076                .is_some_and(|child| child.kind() == ";")
11077    })
11078}
11079
11080fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11081    if node.has_error() {
11082        return false;
11083    }
11084    match node.kind() {
11085        "declaration"
11086        | "field_declaration"
11087        | "alias_declaration"
11088        | "type_definition"
11089        | "static_assert_declaration" => true,
11090        "labeled_statement" => node
11091            .named_child(node.named_child_count().saturating_sub(1))
11092            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11093        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11094            matches!(
11095                child.kind(),
11096                "declaration"
11097                    | "field_declaration"
11098                    | "alias_declaration"
11099                    | "type_definition"
11100                    | "function_definition"
11101            )
11102        }),
11103        _ => false,
11104    }
11105}
11106
11107fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11108    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11109        .then(|| node.child_by_field_name("declarator"))
11110        .flatten()
11111        .and_then(|declarator| extract_variable_name(declarator, source))
11112}
11113
11114fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11115    while let Some(parent) = node.parent() {
11116        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11117            return true;
11118        }
11119        node = parent;
11120    }
11121    false
11122}
11123
11124fn cpp_template_metadata<'tree>(
11125    template_node: Node<'tree>,
11126    declaration_child: Node<'tree>,
11127    source: &str,
11128    ancestry: &ParentIndex<'tree>,
11129) -> Option<CppTemplateMetadata> {
11130    let parameters_node = template_node.child_by_field_name("parameters")?;
11131    let name_node = cpp_templated_class_name_node(declaration_child)?;
11132    let primary_node = match name_node.kind() {
11133        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11134        _ => name_node,
11135    };
11136    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11137    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11138        return None;
11139    }
11140
11141    let mut parameter_nodes = Vec::new();
11142    let mut parameter_names = Vec::new();
11143    let mut cursor = parameters_node.walk();
11144    for parameter in parameters_node.named_children(&mut cursor) {
11145        if !matches!(
11146            parameter.kind(),
11147            "type_parameter_declaration"
11148                | "optional_type_parameter_declaration"
11149                | "variadic_type_parameter_declaration"
11150                | "template_template_parameter_declaration"
11151                | "parameter_declaration"
11152                | "optional_parameter_declaration"
11153                | "variadic_parameter_declaration"
11154        ) {
11155            continue;
11156        }
11157        let index = parameter_nodes.len();
11158        // An unnamed parameter still contributes template arity and kind. Use
11159        // an impossible C++ identifier so positional reconciliation can bind
11160        // it without making source expressions refer to a name that was not
11161        // written.
11162        let name = cpp_template_parameter_name(parameter, source)
11163            .unwrap_or_else(|| format!("<anonymous:{index}>"));
11164        parameter_names.push(name);
11165        parameter_nodes.push(parameter);
11166    }
11167    let parameters = parameter_nodes
11168        .into_iter()
11169        .zip(parameter_names.iter().cloned())
11170        .map(|(parameter, name)| CppTemplateParameterMetadata {
11171            name,
11172            kind: cpp_template_parameter_kind(parameter),
11173            variadic: matches!(
11174                parameter.kind(),
11175                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11176            ),
11177            default: cpp_template_parameter_default_expression(
11178                parameter,
11179                source,
11180                &parameter_names,
11181                ancestry,
11182            ),
11183        })
11184        .collect();
11185    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11186        Vec::new()
11187    } else {
11188        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
11189            .unwrap_or_default()
11190    };
11191    let alias_target = (declaration_child.kind() == "alias_declaration")
11192        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
11193        .flatten();
11194    Some(CppTemplateMetadata {
11195        primary_name,
11196        primary_fq_name: String::new(),
11197        parameters,
11198        specialization_arguments,
11199        alias_target,
11200    })
11201}
11202
11203fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11204    match node.kind() {
11205        "class_specifier" | "struct_specifier" | "union_specifier" => {
11206            node.child_by_field_name("name")
11207        }
11208        "function_definition" => {
11209            let declarator = node.child_by_field_name("declarator")?;
11210            if matches!(declarator.kind(), "identifier" | "template_function") {
11211                Some(declarator)
11212            } else {
11213                None
11214            }
11215        }
11216        "alias_declaration" => node.child_by_field_name("name"),
11217        _ => None,
11218    }
11219}
11220
11221fn cpp_template_alias_target<'tree>(
11222    alias: Node<'tree>,
11223    source: &str,
11224    parameter_names: &[String],
11225    ancestry: &ParentIndex<'tree>,
11226) -> Option<CppTemplateAliasTargetMetadata> {
11227    let mut type_node = alias.child_by_field_name("type")?;
11228    while type_node.kind() == "type_descriptor" {
11229        type_node = type_node.child_by_field_name("type")?;
11230    }
11231    let global = type_node.child_by_field_name("scope").is_none()
11232        && type_node.child(0).is_some_and(|child| child.kind() == "::");
11233    let mut components = Vec::new();
11234    cpp_template_target_components(type_node, source, &mut components)?;
11235    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11236    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11237        components,
11238        global,
11239        arguments,
11240    })
11241}
11242
11243fn cpp_template_target_components(
11244    node: Node<'_>,
11245    source: &str,
11246    out: &mut Vec<String>,
11247) -> Option<()> {
11248    match node.kind() {
11249        "identifier" | "namespace_identifier" | "type_identifier" => {
11250            out.push(node_text(node, source).to_string());
11251            Some(())
11252        }
11253        "template_type" => {
11254            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11255        }
11256        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11257            if let Some(scope) = node.child_by_field_name("scope") {
11258                cpp_template_target_components(scope, source, out)?;
11259            }
11260            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11261        }
11262        _ => None,
11263    }
11264}
11265
11266fn cpp_template_argument_expressions<'tree>(
11267    mut node: Node<'tree>,
11268    source: &str,
11269    parameter_names: &[String],
11270    ancestry: &ParentIndex<'tree>,
11271) -> Option<Vec<CppTemplateExpression>> {
11272    loop {
11273        match node.kind() {
11274            "template_type" | "template_function" => {
11275                let arguments = node.child_by_field_name("arguments")?;
11276                let mut cursor = arguments.walk();
11277                return Some(
11278                    arguments
11279                        .named_children(&mut cursor)
11280                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11281                        .map(|argument| {
11282                            cpp_template_expression(argument, source, parameter_names, ancestry)
11283                        })
11284                        .collect(),
11285                );
11286            }
11287            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11288                node = node
11289                    .child_by_field_name("name")
11290                    .or_else(|| node.child_by_field_name("type"))?;
11291            }
11292            _ => return None,
11293        }
11294    }
11295}
11296
11297fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11298    let candidate = node
11299        .child_by_field_name("name")
11300        .or_else(|| node.child_by_field_name("declarator"))
11301        .or_else(|| {
11302            let mut cursor = node.walk();
11303            node.named_children(&mut cursor).find(|child| {
11304                matches!(
11305                    child.kind(),
11306                    "identifier" | "type_identifier" | "field_identifier"
11307                )
11308            })
11309        })?;
11310    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11311    (!name.is_empty()).then_some(name)
11312}
11313
11314fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11315    match node.kind() {
11316        "type_parameter_declaration"
11317        | "optional_type_parameter_declaration"
11318        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11319        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11320        _ => CppTemplateParameterKind::Value,
11321    }
11322}
11323
11324fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11325    node.child_by_field_name("default_type")
11326        .or_else(|| node.child_by_field_name("default_value"))
11327}
11328
11329fn cpp_template_parameter_default_expression<'tree>(
11330    parameter: Node<'tree>,
11331    source: &str,
11332    parameter_names: &[String],
11333    ancestry: &ParentIndex<'tree>,
11334) -> Option<CppTemplateExpression> {
11335    let default = cpp_template_parameter_default(parameter)?;
11336    let base = cpp_template_expression(default, source, parameter_names, ancestry);
11337    let Some(pointer_error) = parameter.next_named_sibling() else {
11338        return Some(base);
11339    };
11340    let Some(pointer_declarator) =
11341        recovered_abstract_pointer_declarator_term(pointer_error, source)
11342    else {
11343        return Some(base);
11344    };
11345    Some(CppTemplateExpression {
11346        text: format!(
11347            "{}{}",
11348            base.text,
11349            normalize_cpp_whitespace(node_text(pointer_error, source))
11350        ),
11351        term: CppTemplateTerm::Node {
11352            kind: "type_descriptor".to_string(),
11353            children: vec![base.term, pointer_declarator],
11354        },
11355    })
11356}
11357
11358fn recovered_abstract_pointer_declarator_term(
11359    node: Node<'_>,
11360    source: &str,
11361) -> Option<CppTemplateTerm> {
11362    if node.kind() != "ERROR" || node.child_count() == 0 {
11363        return None;
11364    }
11365    let mut children = Vec::new();
11366    for index in 0..node.child_count() {
11367        let child = node.child(index)?;
11368        if child.kind() != "*" {
11369            return None;
11370        }
11371        children.push(CppTemplateTerm::Atom {
11372            kind: "*".to_string(),
11373            text: normalize_cpp_whitespace(node_text(child, source)),
11374        });
11375    }
11376    Some(CppTemplateTerm::Node {
11377        kind: "abstract_pointer_declarator".to_string(),
11378        children,
11379    })
11380}
11381
11382fn cpp_template_expression<'tree>(
11383    node: Node<'tree>,
11384    source: &str,
11385    parameter_names: &[String],
11386    ancestry: &ParentIndex<'tree>,
11387) -> CppTemplateExpression {
11388    let text = normalize_cpp_whitespace(node_text(node, source));
11389    CppTemplateExpression {
11390        text,
11391        term: cpp_template_term(node, source, parameter_names, ancestry),
11392    }
11393}
11394
11395pub fn cpp_template_term<'tree>(
11396    node: Node<'tree>,
11397    source: &str,
11398    parameter_names: &[String],
11399    ancestry: &ParentIndex<'tree>,
11400) -> CppTemplateTerm {
11401    enum Work<'tree> {
11402        Visit(Node<'tree>),
11403        Build { kind: String, child_count: usize },
11404    }
11405
11406    let mut work = vec![Work::Visit(node)];
11407    let mut terms = Vec::new();
11408    while let Some(next) = work.pop() {
11409        match next {
11410            Work::Visit(current) => {
11411                let text = normalize_cpp_whitespace(node_text(current, source));
11412                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11413                    terms.push(CppTemplateTerm::Parameter(text));
11414                    continue;
11415                }
11416                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11417                    let mut cursor = current.walk();
11418                    let named = current
11419                        .named_children(&mut cursor)
11420                        .filter(|child| !child.is_extra() && child.kind() != "comment")
11421                        .collect::<Vec<_>>();
11422                    if let [child] = named.as_slice() {
11423                        work.push(Work::Visit(*child));
11424                        continue;
11425                    }
11426                }
11427                if current.child_count() == 0 {
11428                    terms.push(CppTemplateTerm::Atom {
11429                        kind: if matches!(
11430                            current.kind(),
11431                            "identifier"
11432                                | "type_identifier"
11433                                | "field_identifier"
11434                                | "namespace_identifier"
11435                        ) {
11436                            "identifier".to_string()
11437                        } else {
11438                            current.kind().to_string()
11439                        },
11440                        text,
11441                    });
11442                    continue;
11443                }
11444                let children = (0..current.child_count())
11445                    .filter_map(|index| current.child(index))
11446                    .filter(|child| !child.is_extra() && child.kind() != "comment")
11447                    .collect::<Vec<_>>();
11448                work.push(Work::Build {
11449                    kind: current.kind().to_string(),
11450                    child_count: children.len(),
11451                });
11452                work.extend(children.into_iter().rev().map(Work::Visit));
11453            }
11454            Work::Build { kind, child_count } => {
11455                let children = terms.split_off(terms.len() - child_count);
11456                terms.push(CppTemplateTerm::Node { kind, children });
11457            }
11458        }
11459    }
11460    terms.pop().expect("template term traversal emits one root")
11461}
11462
11463fn cpp_template_term_leaf_is_parameter<'tree>(
11464    node: Node<'tree>,
11465    text: &str,
11466    parameter_names: &[String],
11467    ancestry: &ParentIndex<'tree>,
11468) -> bool {
11469    if !parameter_names.iter().any(|parameter| parameter == text) {
11470        return false;
11471    }
11472    !ancestry.parent(node).is_some_and(|parent| {
11473        matches!(
11474            parent.kind(),
11475            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11476        ) && parent.child_by_field_name("scope").is_some()
11477            && parent.child_by_field_name("name") == Some(node)
11478    })
11479}
11480
11481fn enclosing_cpp_declaration_node<'tree>(
11482    mut node: Node<'tree>,
11483    ancestry: &ParentIndex<'tree>,
11484) -> Option<Node<'tree>> {
11485    loop {
11486        match node.kind() {
11487            "declaration"
11488            | "function_declaration"
11489            | "field_declaration"
11490            | "function_definition" => return Some(node),
11491            _ => node = ancestry.parent(node)?,
11492        }
11493    }
11494}
11495
11496fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11497    let mut params = Vec::new();
11498    let mut cursor = parameters_node.walk();
11499    for child in parameters_node.children(&mut cursor) {
11500        match child.kind() {
11501            "parameter_declaration" | "optional_parameter_declaration" => {
11502                params.push(cpp_parameter_type(child, source));
11503            }
11504            "variadic_parameter_declaration" => {
11505                params.push(cpp_parameter_type(child, source));
11506            }
11507            "variadic_parameter" | "..." => params.push("...".to_string()),
11508            _ => {}
11509        }
11510    }
11511
11512    if params.is_empty() {
11513        "()".to_string()
11514    } else {
11515        format!("({})", params.join(", "))
11516    }
11517}
11518
11519fn cpp_signature_metadata<'tree>(
11520    signature: String,
11521    function_declarator: Node<'tree>,
11522    source: &str,
11523    ancestry: &ParentIndex<'tree>,
11524) -> SignatureMetadata {
11525    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
11526    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
11527    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
11528    let return_type_identity =
11529        cpp_callable_return_type_identity(function_declarator, source, ancestry);
11530    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
11531        return enrich(
11532            SignatureMetadata::new(signature, Vec::new())
11533                .with_return_type_text(return_type_text)
11534                .with_return_type_identity(return_type_identity),
11535        );
11536    };
11537    let callable_arity = cpp_callable_arity(parameters_node, source);
11538    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
11539    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
11540    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
11541    let Some(relative_start) = signature
11542        .get(search_from..)
11543        .and_then(|suffix| suffix.find(&parameter_text))
11544    else {
11545        return enrich(
11546            SignatureMetadata::new(signature, Vec::new())
11547                .with_callable_arity(callable_arity)
11548                .with_callable_parameter_types(callable_parameter_types)
11549                .with_return_type_text(return_type_text)
11550                .with_return_type_identity(return_type_identity),
11551        );
11552    };
11553    let parameters_start = search_from + relative_start;
11554    let parameters_end = parameters_start + parameter_text.len();
11555    let mut search_start = parameters_start;
11556    let parameters = cpp_parameter_label_nodes(parameters_node)
11557        .into_iter()
11558        .filter_map(|label_node| {
11559            let label = normalize_cpp_whitespace(node_text(label_node, source));
11560            if label.is_empty() || search_start > parameters_end {
11561                return None;
11562            }
11563            let haystack = signature.get(search_start..parameters_end)?;
11564            let relative_start = haystack.find(&label)?;
11565            let start_byte = search_start + relative_start;
11566            let end_byte = start_byte + label.len();
11567            search_start = end_byte;
11568            Some(ParameterMetadata::new(label, start_byte, end_byte))
11569        })
11570        .collect();
11571    enrich(
11572        SignatureMetadata::new(signature, parameters)
11573            .with_callable_arity(callable_arity)
11574            .with_callable_parameter_types(callable_parameter_types)
11575            .with_return_type_text(return_type_text)
11576            .with_return_type_identity(return_type_identity),
11577    )
11578}
11579
11580fn cpp_callable_is_structural_constructor<'tree>(
11581    function_declarator: Node<'tree>,
11582    source: &str,
11583    ancestry: &ParentIndex<'tree>,
11584) -> bool {
11585    let Some(name_node) = function_declarator
11586        .child_by_field_name("declarator")
11587        .or_else(|| function_declarator.child_by_field_name("name"))
11588        .or_else(|| last_named_child(function_declarator))
11589    else {
11590        return false;
11591    };
11592    let Some(callable_name) = direct_identifier_name(name_node, source) else {
11593        return false;
11594    };
11595
11596    let mut current = ancestry.parent(function_declarator);
11597    while let Some(ancestor) = current {
11598        let owner_name = match ancestor.kind() {
11599            "class_specifier" | "struct_specifier" | "union_specifier" => {
11600                class_like_name(ancestor, source, ancestry)
11601            }
11602            "ERROR" => malformed_class_error_owner_name(ancestor, source),
11603            _ => None,
11604        };
11605        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
11606            return true;
11607        }
11608        current = ancestry.parent(ancestor);
11609    }
11610    false
11611}
11612
11613/// Recover the owner name from the direct grammar shape retained when a later
11614/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
11615/// `ERROR` node:
11616///
11617/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
11618///
11619/// Direct-child checks keep this distinct from an unrelated nested class inside
11620/// a broader error region. The closing brace may be displaced past the error
11621/// node, so the opening body token is the available structural boundary.
11622fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
11623    if node.kind() != "ERROR" {
11624        return None;
11625    }
11626    let keyword = node.child(0)?;
11627    if !matches!(keyword.kind(), "class" | "struct" | "union") {
11628        return None;
11629    }
11630    let name_node = node.child(1)?;
11631    let name = direct_identifier_name(name_node, source)?;
11632    let has_body = (2..node.child_count())
11633        .filter_map(|index| node.child(index))
11634        .any(|child| child.kind() == "{");
11635    has_body.then_some(name)
11636}
11637
11638/// The parser-derived return type of one callable declaration.
11639///
11640/// This accepts the enclosing declaration node so consumers do not need to
11641/// duplicate the declarator-unwrapping rules before asking for the structured
11642/// identity.
11643pub fn cpp_callable_declaration_return_type_identity<'tree>(
11644    callable: Node<'tree>,
11645    source: &str,
11646    ancestry: &ParentIndex<'tree>,
11647) -> Option<StructuredTypeIdentity> {
11648    let declarator = callable
11649        .child_by_field_name("declarator")
11650        .and_then(extract_function_declarator)?;
11651    cpp_callable_return_type_identity(declarator, source, ancestry)
11652}
11653
11654pub(crate) fn cpp_callable_return_type_identity<'tree>(
11655    function_declarator: Node<'tree>,
11656    source: &str,
11657    ancestry: &ParentIndex<'tree>,
11658) -> Option<StructuredTypeIdentity> {
11659    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
11660        return None;
11661    }
11662    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
11663    if let Some((return_type, _)) =
11664        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
11665    {
11666        return cpp_structured_type_identity(return_type, source, &lexical_scope);
11667    }
11668    let mut cursor = function_declarator.walk();
11669    if let Some(trailing) = function_declarator
11670        .named_children(&mut cursor)
11671        .find(|child| child.kind() == "trailing_return_type")
11672        && let Some(type_descriptor) = trailing.named_child(0)
11673    {
11674        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
11675    }
11676
11677    let mut current = function_declarator;
11678    let mut wrappers = Vec::new();
11679    while let Some(parent) = ancestry.parent(current) {
11680        if matches!(
11681            parent.kind(),
11682            "function_definition" | "declaration" | "field_declaration"
11683        ) {
11684            let type_node = parent.child_by_field_name("type")?;
11685            if cpp_export_macro_token(node_text(type_node, source))
11686                && (0..parent.named_child_count()).any(|index| {
11687                    parent
11688                        .named_child(index)
11689                        .is_some_and(|child| child.kind() == "ERROR")
11690                })
11691            {
11692                return None;
11693            }
11694            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
11695            for wrapper in wrappers.into_iter().rev() {
11696                identity = cpp_wrap_structured_type(identity, wrapper)?;
11697            }
11698            return Some(identity);
11699        }
11700        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
11701            || (matches!(
11702                parent.kind(),
11703                "pointer_declarator"
11704                    | "reference_declarator"
11705                    | "array_declarator"
11706                    | "parenthesized_declarator"
11707            ) && parent.named_child_count() == 1
11708                && parent.named_child(0) == Some(current));
11709        if !wraps_current_declarator {
11710            return None;
11711        }
11712        match parent.kind() {
11713            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
11714            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
11715            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
11716            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
11717            _ => return None,
11718        }
11719        current = parent;
11720    }
11721    None
11722}
11723
11724fn cpp_structured_type_identity(
11725    node: Node<'_>,
11726    source: &str,
11727    lexical_scope: &[String],
11728) -> Option<StructuredTypeIdentity> {
11729    enum Work<'tree> {
11730        Visit(Node<'tree>),
11731        Wrap(CppStructuredTypeWrapper),
11732        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
11733        BuildGeneric { argument_count: usize },
11734    }
11735
11736    let mut work = vec![Work::Visit(node)];
11737    let mut values = Vec::new();
11738    let mut builder = StructuredTypeIdentityBuilder::default();
11739    while let Some(next) = work.pop() {
11740        match next {
11741            Work::Visit(current) => match current.kind() {
11742                "type_descriptor" => {
11743                    let type_node = current
11744                        .child_by_field_name("type")
11745                        .or_else(|| current.named_child(0))?;
11746                    let mut wrappers = Vec::new();
11747                    let mut cursor = current.walk();
11748                    for child in current.named_children(&mut cursor) {
11749                        if child.id() != type_node.id() {
11750                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
11751                        }
11752                    }
11753                    work.push(Work::ApplyWrappers(wrappers));
11754                    work.push(Work::Visit(type_node));
11755                }
11756                "pointer_declarator" | "abstract_pointer_declarator" => {
11757                    let child = current
11758                        .child_by_field_name("declarator")
11759                        .or_else(|| current.named_child(0))?;
11760                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
11761                    work.push(Work::Visit(child));
11762                }
11763                "reference_declarator" => {
11764                    let child = current
11765                        .child_by_field_name("declarator")
11766                        .or_else(|| current.named_child(0))?;
11767                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
11768                    work.push(Work::Visit(child));
11769                }
11770                "array_declarator" | "abstract_array_declarator" => {
11771                    let child = current
11772                        .child_by_field_name("declarator")
11773                        .or_else(|| current.named_child(0))?;
11774                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
11775                    work.push(Work::Visit(child));
11776                }
11777                "template_type" => {
11778                    let name_node = current.child_by_field_name("name")?;
11779                    let arguments = current
11780                        .child_by_field_name("arguments")
11781                        .map(|arguments_node| {
11782                            let mut cursor = arguments_node.walk();
11783                            arguments_node
11784                                .named_children(&mut cursor)
11785                                .filter(|child| !child.is_extra() && child.kind() != "comment")
11786                                .collect::<Vec<_>>()
11787                        })
11788                        .unwrap_or_default();
11789                    work.push(Work::BuildGeneric {
11790                        argument_count: arguments.len(),
11791                    });
11792                    work.extend(arguments.into_iter().rev().map(Work::Visit));
11793                    work.push(Work::Visit(name_node));
11794                }
11795                "qualified_identifier"
11796                | "scoped_identifier"
11797                | "scoped_type_identifier"
11798                | "type_identifier"
11799                | "field_identifier"
11800                | "identifier"
11801                | "namespace_identifier"
11802                | "primitive_type" => {
11803                    values.push(builder.named(cpp_structured_named_type(
11804                        current,
11805                        source,
11806                        lexical_scope,
11807                    )?)?);
11808                }
11809                _ => {
11810                    let child = current.child_by_field_name("type").or_else(|| {
11811                        (current.named_child_count() == 1)
11812                            .then(|| current.named_child(0))
11813                            .flatten()
11814                    })?;
11815                    work.push(Work::Visit(child));
11816                }
11817            },
11818            Work::Wrap(wrapper) => {
11819                let root = values.pop()?;
11820                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
11821            }
11822            Work::ApplyWrappers(wrappers) => {
11823                let mut root = values.pop()?;
11824                for wrapper in wrappers.into_iter().rev() {
11825                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
11826                }
11827                values.push(root);
11828            }
11829            Work::BuildGeneric { argument_count } => {
11830                let value_count = argument_count.checked_add(1)?;
11831                let start = values.len().checked_sub(value_count)?;
11832                let mut built = values.split_off(start);
11833                let base = built.remove(0);
11834                values.push(builder.generic(base, built)?);
11835            }
11836        }
11837    }
11838    (values.len() == 1)
11839        .then(|| values.pop())
11840        .flatten()
11841        .and_then(|root| builder.finish(root))
11842}
11843
11844fn cpp_structured_named_type(
11845    node: Node<'_>,
11846    source: &str,
11847    lexical_scope: &[String],
11848) -> Option<StructuredTypeName> {
11849    let path = cpp_structured_type_path(node, source)?;
11850    let absolute = node.child_by_field_name("scope").is_none()
11851        && node.child(0).is_some_and(|child| child.kind() == "::");
11852    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
11853}
11854
11855#[derive(Clone, Copy)]
11856enum CppStructuredTypeWrapper {
11857    Pointer,
11858    LvalueReference,
11859    RvalueReference,
11860    Array,
11861}
11862
11863fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
11864    let mut wrappers = Vec::new();
11865    let mut current = node;
11866    loop {
11867        match current.kind() {
11868            "pointer_declarator" | "abstract_pointer_declarator" => {
11869                wrappers.push(CppStructuredTypeWrapper::Pointer)
11870            }
11871            "reference_declarator" | "abstract_reference_declarator" => {
11872                wrappers.push(cpp_reference_wrapper(current)?);
11873            }
11874            "array_declarator" | "abstract_array_declarator" => {
11875                wrappers.push(CppStructuredTypeWrapper::Array)
11876            }
11877            _ => break,
11878        }
11879        let Some(child) = current
11880            .child_by_field_name("declarator")
11881            .or_else(|| current.named_child(0))
11882        else {
11883            break;
11884        };
11885        current = child;
11886    }
11887    Some(wrappers)
11888}
11889
11890fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
11891    node.children(&mut node.walk())
11892        .find_map(|child| match child.kind() {
11893            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
11894            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
11895            _ => None,
11896        })
11897}
11898
11899fn cpp_wrap_structured_type(
11900    identity: StructuredTypeIdentity,
11901    wrapper: CppStructuredTypeWrapper,
11902) -> Option<StructuredTypeIdentity> {
11903    match wrapper {
11904        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
11905        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
11906        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
11907        CppStructuredTypeWrapper::Array => identity.wrap_array(),
11908    }
11909}
11910
11911fn cpp_wrap_structured_type_node(
11912    builder: &mut StructuredTypeIdentityBuilder,
11913    inner: StructuredTypeNodeId,
11914    wrapper: CppStructuredTypeWrapper,
11915) -> Option<StructuredTypeNodeId> {
11916    match wrapper {
11917        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
11918        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
11919        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
11920        CppStructuredTypeWrapper::Array => builder.array(inner),
11921    }
11922}
11923
11924fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
11925    let mut path = Vec::new();
11926    let mut stack = vec![node];
11927    while let Some(current) = stack.pop() {
11928        match current.kind() {
11929            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
11930                let component = node_text(current, source).to_string();
11931                if component.is_empty() {
11932                    return None;
11933                }
11934                path.push(component);
11935            }
11936            "template_type" | "dependent_type" => {
11937                stack.push(current.child_by_field_name("name")?);
11938            }
11939            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11940                stack.push(current.child_by_field_name("name")?);
11941                if let Some(scope) = current.child_by_field_name("scope") {
11942                    stack.push(scope);
11943                }
11944            }
11945            _ => return None,
11946        }
11947    }
11948    (!path.is_empty()).then_some(path)
11949}
11950
11951fn cpp_callable_lexical_scope<'tree>(
11952    node: Node<'tree>,
11953    source: &str,
11954    ancestry: &ParentIndex<'tree>,
11955) -> Vec<String> {
11956    let mut groups = Vec::new();
11957    let mut current = ancestry.parent(node);
11958    while let Some(parent) = current {
11959        if matches!(
11960            parent.kind(),
11961            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
11962        ) && let Some(name_node) = parent.child_by_field_name("name")
11963            && let Some(components) = cpp_structured_type_path(name_node, source)
11964            && !components.is_empty()
11965        {
11966            groups.push(components);
11967        }
11968        current = ancestry.parent(parent);
11969    }
11970    groups.reverse();
11971    groups.into_iter().flatten().collect()
11972}
11973
11974fn cpp_callable_dispatch_extensibility<'tree>(
11975    function_declarator: Node<'tree>,
11976    ancestry: &ParentIndex<'tree>,
11977) -> DispatchExtensibility {
11978    let mut declaration = None;
11979    let mut current = Some(function_declarator);
11980    while let Some(node) = current {
11981        match node.kind() {
11982            "template_declaration"
11983            | "preproc_if"
11984            | "preproc_ifdef"
11985            | "preproc_else"
11986            | "preproc_elif"
11987            | "preproc_call"
11988            | "ERROR" => return DispatchExtensibility::Open,
11989            "declaration" | "field_declaration" | "function_definition" => {
11990                declaration.get_or_insert(node);
11991            }
11992            "translation_unit" => break,
11993            _ => {}
11994        }
11995        current = ancestry.parent(node);
11996    }
11997    let Some(declaration) = declaration else {
11998        return DispatchExtensibility::Open;
11999    };
12000
12001    let mut saw_virtual_boundary = false;
12002    let mut stack = vec![declaration];
12003    while let Some(node) = stack.pop() {
12004        match node.kind() {
12005            "compound_statement" | "field_declaration_list" => continue,
12006            "final" | "final_specifier" => return DispatchExtensibility::Closed,
12007            "virtual"
12008            | "override"
12009            | "virtual_specifier"
12010            | "pure_virtual_clause"
12011            | "template_parameter_list"
12012            | "template_method"
12013            | "template_function"
12014            | "ERROR" => saw_virtual_boundary = true,
12015            _ => {}
12016        }
12017        let mut cursor = node.walk();
12018        stack.extend(node.children(&mut cursor));
12019    }
12020
12021    if saw_virtual_boundary {
12022        DispatchExtensibility::Open
12023    } else {
12024        DispatchExtensibility::Closed
12025    }
12026}
12027
12028fn cpp_callable_linkage<'tree>(
12029    declaration: Node<'tree>,
12030    source: &str,
12031    ancestry: &ParentIndex<'tree>,
12032) -> CallableLinkage {
12033    let mut enclosed_by_class = false;
12034    let mut current = ancestry.parent(declaration);
12035    while let Some(node) = current {
12036        if node.kind() == "namespace_definition"
12037            && node
12038                .child_by_field_name("name")
12039                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12040        {
12041            return CallableLinkage::Internal;
12042        }
12043        if matches!(
12044            node.kind(),
12045            "class_specifier" | "struct_specifier" | "union_specifier"
12046        ) {
12047            if node
12048                .child_by_field_name("name")
12049                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12050            {
12051                return CallableLinkage::Internal;
12052            }
12053            enclosed_by_class = true;
12054        }
12055        // An export macro between `class` and the class name can make
12056        // tree-sitter parse the entire class as a function definition. The
12057        // structured recovery proves that this container is a named class
12058        // scope, not a local callable scope.
12059        if node.kind() == "lambda_expression"
12060            || node.kind() == "function_definition"
12061                && !is_recovered_exported_class_container(node, source)
12062        {
12063            return CallableLinkage::Internal;
12064        }
12065        current = ancestry.parent(node);
12066    }
12067
12068    if enclosed_by_class {
12069        return CallableLinkage::External;
12070    }
12071
12072    let mut cursor = declaration.walk();
12073    if declaration.named_children(&mut cursor).any(|child| {
12074        child.kind() == "storage_class_specifier"
12075            && normalize_cpp_whitespace(node_text(child, source)) == "static"
12076    }) {
12077        CallableLinkage::Internal
12078    } else {
12079        CallableLinkage::External
12080    }
12081}
12082
12083fn cpp_callable_return_type_text<'tree>(
12084    function_declarator: Node<'tree>,
12085    source: &str,
12086    ancestry: &ParentIndex<'tree>,
12087) -> Option<String> {
12088    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12089        return None;
12090    }
12091    if let Some((return_type, _)) =
12092        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12093    {
12094        let text = normalize_cpp_whitespace(node_text(return_type, source));
12095        return (!text.is_empty()).then_some(text);
12096    }
12097    let mut cursor = function_declarator.walk();
12098    if let Some(trailing) = function_declarator
12099        .named_children(&mut cursor)
12100        .find(|child| child.kind() == "trailing_return_type")
12101        && let Some(type_descriptor) = trailing.named_child(0)
12102    {
12103        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12104        if !text.is_empty() {
12105            return Some(text);
12106        }
12107    }
12108
12109    let mut current = function_declarator;
12110    let mut indirection = String::new();
12111    while let Some(parent) = ancestry.parent(current) {
12112        if matches!(
12113            parent.kind(),
12114            "function_definition" | "declaration" | "field_declaration"
12115        ) {
12116            let type_node = parent.child_by_field_name("type")?;
12117            if cpp_export_macro_token(node_text(type_node, source))
12118                && (0..parent.named_child_count()).any(|index| {
12119                    parent
12120                        .named_child(index)
12121                        .is_some_and(|child| child.kind() == "ERROR")
12122                })
12123            {
12124                // Export/decorator macros commonly occupy the grammar's `type`
12125                // field and leave the semantic return type in an ERROR sibling.
12126                // Do not persist the macro token as a return type. The malformed
12127                // declaration does not carry enough structured evidence here.
12128                return None;
12129            }
12130            let base = normalize_cpp_whitespace(node_text(type_node, source));
12131            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12132        }
12133        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12134            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12135                && parent.named_child_count() == 1
12136                && parent.named_child(0) == Some(current));
12137        if wraps_current_declarator {
12138            match parent.kind() {
12139                "pointer_declarator" => indirection.push('*'),
12140                "reference_declarator" => {
12141                    let reference = parent
12142                        .children(&mut parent.walk())
12143                        .find(|child| !child.is_named())
12144                        .map(|child| node_text(child, source))
12145                        .unwrap_or("&");
12146                    indirection.push_str(reference);
12147                }
12148                "init_declarator" | "parenthesized_declarator" => {}
12149                _ => return None,
12150            }
12151            current = parent;
12152            continue;
12153        }
12154        return None;
12155    }
12156    None
12157}
12158
12159fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12160    let mut required = 0;
12161    let mut total = 0;
12162    let mut repeated = false;
12163    let mut cursor = parameters_node.walk();
12164    for child in parameters_node.children(&mut cursor) {
12165        match child.kind() {
12166            "parameter_declaration" => {
12167                if cpp_parameter_is_explicit_object(child, source) {
12168                    continue;
12169                }
12170                if child.child_by_field_name("declarator").is_none()
12171                    && child
12172                        .child_by_field_name("type")
12173                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12174                {
12175                    continue;
12176                }
12177                required += 1;
12178                total += 1;
12179            }
12180            "optional_parameter_declaration" => total += 1,
12181            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12182                repeated = true;
12183            }
12184            _ => {}
12185        }
12186    }
12187    CallableArity::new(required, total, repeated)
12188}
12189
12190fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12191    parameter
12192        .child_by_field_name("type")
12193        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12194        .and_then(|type_node| type_node.child_by_field_name("constraint"))
12195        .is_some_and(|constraint| {
12196            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12197        })
12198}
12199
12200/// One entry of a callable's invocation parameter list.
12201///
12202/// The list excludes an explicit object parameter and a lone `void`, so its
12203/// length is the callable's invocation arity. Every derivation of a parameter
12204/// type - the rendered spelling used for overload discrimination and the
12205/// structured identity used by dependency-pack production - starts from this
12206/// same sequence, so the two can never disagree about which parameters exist.
12207#[derive(Clone, Copy)]
12208enum CppParameterSlot<'tree> {
12209    Declared(Node<'tree>),
12210    Ellipsis,
12211}
12212
12213fn cpp_callable_parameter_slots<'tree>(
12214    parameters_node: Node<'tree>,
12215    source: &str,
12216) -> Vec<CppParameterSlot<'tree>> {
12217    let mut slots = Vec::new();
12218    let mut cursor = parameters_node.walk();
12219    for parameter in parameters_node.children(&mut cursor) {
12220        match parameter.kind() {
12221            "parameter_declaration" | "optional_parameter_declaration" => {
12222                if cpp_parameter_is_explicit_object(parameter, source)
12223                    || (parameter.child_by_field_name("declarator").is_none()
12224                        && parameter
12225                            .child_by_field_name("type")
12226                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12227                {
12228                    continue;
12229                }
12230                slots.push(CppParameterSlot::Declared(parameter));
12231            }
12232            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12233                slots.push(CppParameterSlot::Ellipsis);
12234            }
12235            _ => {}
12236        }
12237    }
12238    slots
12239}
12240
12241fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12242    cpp_callable_parameter_slots(parameters_node, source)
12243        .into_iter()
12244        .map(|slot| match slot {
12245            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12246            CppParameterSlot::Ellipsis => "...".to_string(),
12247        })
12248        .collect()
12249}
12250
12251/// One callable parameter's parser-derived type.
12252///
12253/// A rendered spelling such as `const T&` is a source text, not a type name. A
12254/// consumer that must publish a type into a structured model - a semantic-pack
12255/// type reference, for example - reads this instead.
12256#[derive(Debug, Clone, PartialEq, Eq)]
12257pub enum CppParameterType {
12258    /// The written type reduced to a structured identity. C++ cv-qualifiers
12259    /// have no place in that model and are not represented.
12260    Structured(StructuredTypeIdentity),
12261    /// A `...` pack, which declares no parameter type at all.
12262    Ellipsis,
12263    /// A written type with no structured reduction, such as a macro-obscured,
12264    /// `decltype`-computed, or function-pointer parameter.
12265    Unstructured,
12266}
12267
12268/// The structured type of each invocation parameter, in declaration order.
12269///
12270/// The result is index-parallel with the rendered
12271/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
12272/// callable.
12273pub fn cpp_callable_parameter_type_identities<'tree>(
12274    function_declarator: Node<'tree>,
12275    source: &str,
12276    ancestry: &ParentIndex<'tree>,
12277) -> Vec<CppParameterType> {
12278    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12279        return Vec::new();
12280    };
12281    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12282    cpp_callable_parameter_slots(parameters_node, source)
12283        .into_iter()
12284        .map(|slot| match slot {
12285            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12286            CppParameterSlot::Declared(parameter) => {
12287                cpp_parameter_type_identity(parameter, source, &lexical_scope)
12288                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12289            }
12290        })
12291        .collect()
12292}
12293
12294/// The parser-derived type of one declared object or parameter.
12295///
12296/// The declaration owns the base `type` field while the individual declarator
12297/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
12298/// lets callers distinguish several declarators in one declaration without
12299/// reparsing a rendered signature.
12300pub fn cpp_declaration_type_identity<'tree>(
12301    declaration: Node<'tree>,
12302    declarator: Node<'tree>,
12303    source: &str,
12304    ancestry: &ParentIndex<'tree>,
12305) -> Option<StructuredTypeIdentity> {
12306    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12307    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12308}
12309
12310fn cpp_parameter_type_identity(
12311    parameter: Node<'_>,
12312    source: &str,
12313    lexical_scope: &[String],
12314) -> Option<StructuredTypeIdentity> {
12315    cpp_declaration_type_identity_in_scope(
12316        parameter,
12317        cpp_parameter_declarator(parameter),
12318        source,
12319        lexical_scope,
12320    )
12321}
12322
12323fn cpp_declaration_type_identity_in_scope(
12324    declaration: Node<'_>,
12325    declarator: Option<Node<'_>>,
12326    source: &str,
12327    lexical_scope: &[String],
12328) -> Option<StructuredTypeIdentity> {
12329    let type_node = declaration.child_by_field_name("type")?;
12330    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12331    if let Some(declarator) = declarator {
12332        for wrapper in cpp_structured_declarator_wrappers(declarator)?
12333            .into_iter()
12334            .rev()
12335        {
12336            identity = cpp_wrap_structured_type(identity, wrapper)?;
12337        }
12338    }
12339    Some(identity)
12340}
12341
12342/// One callable parameter's comparable shape.
12343///
12344/// [`CppParameterType`] above answers "which type is written here" for a
12345/// structured model and deliberately records no cv-qualifiers, so it reports
12346/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
12347/// callable declarations declare one function needs the opposite trade: every
12348/// cv-qualifier that C++ counts as part of the parameter type must survive,
12349/// while the two declarations may spell the same type through different
12350/// qualifications. This slot carries that comparand.
12351///
12352/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
12353/// and with the rendered parameter spellings of the same callable.
12354#[derive(Debug, Clone, PartialEq, Eq)]
12355pub enum CppComparableSlot {
12356    /// A declared parameter reduced to its comparable shape.
12357    Shape(CppComparableParameter),
12358    /// A `...` pack, which declares no parameter type at all.
12359    Ellipsis,
12360    /// A parameter with no comparable reduction, such as a macro-obscured,
12361    /// `decltype`-computed, or function-pointer parameter.
12362    Unstructured,
12363}
12364
12365/// A parameter type as a flat arena of nodes plus a root index.
12366///
12367/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
12368/// can nest types very deeply, and cloning, comparing or dropping the value
12369/// must not consume the Rust call stack. Nodes are appended in post-order, so
12370/// every child index is smaller than its parent's and the last appended node is
12371/// the root.
12372///
12373/// That post-order append is also what makes the derived `PartialEq` a correct
12374/// structural equality: the builder below is deterministic, so one type shape
12375/// has exactly one arena layout no matter which spelling produced it. Two
12376/// shapes are equal as values iff they are equal as type trees.
12377#[derive(Debug, Clone, PartialEq, Eq)]
12378pub struct CppComparableParameter {
12379    nodes: Vec<CppComparableNode>,
12380    root: usize,
12381}
12382
12383/// One node of a [`CppComparableParameter`] arena.
12384///
12385/// `Reference` and `Array` carry no qualifiers because the grammar writes none
12386/// on them: a reference cannot be cv-qualified in C++, and an array's
12387/// qualifiers belong to its element type. A cv-qualifier written on a generic
12388/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
12389/// which is the only Named node the whole spelling produces.
12390#[derive(Debug, Clone, PartialEq, Eq)]
12391pub enum CppComparableNode {
12392    Named {
12393        name: StructuredTypeName,
12394        primitive: bool,
12395        konst: bool,
12396        volatil: bool,
12397    },
12398    Pointer {
12399        inner: usize,
12400        konst: bool,
12401        volatil: bool,
12402    },
12403    Reference {
12404        inner: usize,
12405    },
12406    Array {
12407        inner: usize,
12408    },
12409    Generic {
12410        base: usize,
12411        arguments: Vec<usize>,
12412    },
12413}
12414
12415impl CppComparableParameter {
12416    pub fn root(&self) -> usize {
12417        self.root
12418    }
12419
12420    pub fn node(&self, index: usize) -> &CppComparableNode {
12421        &self.nodes[index]
12422    }
12423
12424    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
12425    /// parameter's top level only.
12426    ///
12427    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
12428    /// declare one function, and a top-level array type becomes a pointer to
12429    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
12430    /// type constructor is this arena's root, which is why both adjustments
12431    /// are one match on it: cv on an inner pointer level, on a pointee, or on
12432    /// an array element keeps distinguishing the type, and an array behind a
12433    /// pointer or reference is not a top-level array.
12434    fn adjust_parameter_top_level(&mut self) {
12435        let root = self.root;
12436        match &mut self.nodes[root] {
12437            CppComparableNode::Named { konst, volatil, .. }
12438            | CppComparableNode::Pointer { konst, volatil, .. } => {
12439                *konst = false;
12440                *volatil = false;
12441            }
12442            CppComparableNode::Array { inner } => {
12443                let inner = *inner;
12444                self.nodes[root] = CppComparableNode::Pointer {
12445                    inner,
12446                    konst: false,
12447                    volatil: false,
12448                };
12449            }
12450            CppComparableNode::Generic { base, .. } => {
12451                let base = *base;
12452                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
12453                    unreachable!("a comparable generic's base is always a named leaf");
12454                };
12455                *konst = false;
12456                *volatil = false;
12457            }
12458            CppComparableNode::Reference { .. } => {}
12459        }
12460    }
12461}
12462
12463/// The comparable shape of each invocation parameter, in declaration order.
12464///
12465/// The result is index-parallel with
12466/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
12467/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
12468/// must treat as evidence of nothing rather than as agreement.
12469pub fn cpp_comparable_parameter_shapes<'tree>(
12470    function_declarator: Node<'tree>,
12471    source: &str,
12472    ancestry: &ParentIndex<'tree>,
12473) -> Vec<CppComparableSlot> {
12474    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12475        return Vec::new();
12476    };
12477    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12478    cpp_callable_parameter_slots(parameters_node, source)
12479        .into_iter()
12480        .map(|slot| match slot {
12481            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
12482            CppParameterSlot::Declared(parameter) => {
12483                cpp_comparable_parameter(parameter, source, &lexical_scope)
12484                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
12485            }
12486        })
12487        .collect()
12488}
12489
12490fn cpp_comparable_parameter(
12491    parameter: Node<'_>,
12492    source: &str,
12493    lexical_scope: &[String],
12494) -> Option<CppComparableParameter> {
12495    let type_node = parameter.child_by_field_name("type")?;
12496    let levels = match cpp_parameter_declarator(parameter) {
12497        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12498        None => Vec::new(),
12499    };
12500    let mut shape = cpp_comparable_type_shape(
12501        type_node,
12502        cpp_cv_qualifiers(parameter, source),
12503        levels,
12504        source,
12505        lexical_scope,
12506    )?;
12507    shape.adjust_parameter_top_level();
12508    Some(shape)
12509}
12510
12511/// The `const` and `volatile` qualifiers written as direct named children of
12512/// `node`.
12513///
12514/// The grammar exposes `type_qualifier` as a non-field named child in exactly
12515/// the three places a parameter's qualifiers can be written: on the
12516/// `parameter_declaration` itself (the base type), on a `type_descriptor`
12517/// (inside a template argument list), and on each `pointer_declarator` level
12518/// (the pointer object). Every other qualifier the grammar admits - `restrict`
12519/// and friends - takes no part in C++ type identity, the same filter
12520/// `cpp_parameter_type` applies to the rendered spelling (#1827).
12521fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
12522    let mut qualifiers = CppCvQualifiers::default();
12523    let mut cursor = node.walk();
12524    for child in node.named_children(&mut cursor) {
12525        if child.kind() != "type_qualifier" {
12526            continue;
12527        }
12528        match node_text(child, source) {
12529            "const" => qualifiers.konst = true,
12530            "volatile" => qualifiers.volatil = true,
12531            _ => {}
12532        }
12533    }
12534    qualifiers
12535}
12536
12537#[derive(Clone, Copy, Default)]
12538struct CppCvQualifiers {
12539    konst: bool,
12540    volatil: bool,
12541}
12542
12543impl CppCvQualifiers {
12544    fn union(self, other: Self) -> Self {
12545        Self {
12546            konst: self.konst || other.konst,
12547            volatil: self.volatil || other.volatil,
12548        }
12549    }
12550}
12551
12552/// One pointer, reference or array level a declarator chain adds.
12553#[derive(Clone, Copy)]
12554enum CppComparableLevel {
12555    Pointer { konst: bool, volatil: bool },
12556    Reference,
12557    Array,
12558}
12559
12560/// The levels `declarator` adds, outermost written level first.
12561///
12562/// C++ declarator syntax binds inside out: the level written closest to the
12563/// declared name is the outermost type constructor, and tree-sitter nests it
12564/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
12565/// applies in order to reach "array of pointer to int", and the qualifier of
12566/// `int * const *p` is read on the level it was written next to, the inner
12567/// pointer of the resulting type.
12568///
12569/// A declarator chain that names a function type - a function-pointer
12570/// parameter - has no comparable shape and reports `None`, matching the
12571/// structured identity channel.
12572fn cpp_comparable_declarator_levels(
12573    declarator: Node<'_>,
12574    source: &str,
12575) -> Option<Vec<CppComparableLevel>> {
12576    let mut levels = Vec::new();
12577    let mut current = declarator;
12578    loop {
12579        match current.kind() {
12580            "pointer_declarator" | "abstract_pointer_declarator" => {
12581                let qualifiers = cpp_cv_qualifiers(current, source);
12582                levels.push(CppComparableLevel::Pointer {
12583                    konst: qualifiers.konst,
12584                    volatil: qualifiers.volatil,
12585                });
12586            }
12587            "reference_declarator" | "abstract_reference_declarator" => {
12588                levels.push(CppComparableLevel::Reference);
12589            }
12590            "array_declarator" | "abstract_array_declarator" => {
12591                levels.push(CppComparableLevel::Array);
12592            }
12593            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
12594            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
12595            _ => return None,
12596        }
12597        let Some(next) = cpp_nested_declarator(current) else {
12598            return Some(levels);
12599        };
12600        current = next;
12601    }
12602}
12603
12604/// Reduce one written type to a comparable arena.
12605///
12606/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
12607/// two additions: each visited type node carries the cv-qualifiers written on
12608/// it, and declarator levels arrive as a prepared list rather than being
12609/// rediscovered inside the walk.
12610fn cpp_comparable_type_shape(
12611    type_node: Node<'_>,
12612    qualifiers: CppCvQualifiers,
12613    levels: Vec<CppComparableLevel>,
12614    source: &str,
12615    lexical_scope: &[String],
12616) -> Option<CppComparableParameter> {
12617    enum Work<'tree> {
12618        Visit {
12619            node: Node<'tree>,
12620            qualifiers: CppCvQualifiers,
12621        },
12622        ApplyLevels(Vec<CppComparableLevel>),
12623        BuildGeneric {
12624            argument_count: usize,
12625        },
12626    }
12627
12628    let mut nodes: Vec<CppComparableNode> = Vec::new();
12629    let mut values: Vec<usize> = Vec::new();
12630    let mut work = vec![
12631        Work::ApplyLevels(levels),
12632        Work::Visit {
12633            node: type_node,
12634            qualifiers,
12635        },
12636    ];
12637    while let Some(next) = work.pop() {
12638        match next {
12639            Work::Visit { node, qualifiers } => match node.kind() {
12640                "type_descriptor" => {
12641                    let inner_type = node
12642                        .child_by_field_name("type")
12643                        .or_else(|| node.named_child(0))?;
12644                    let mut cursor = node.walk();
12645                    let declarator = node.child_by_field_name("declarator").or_else(|| {
12646                        node.named_children(&mut cursor).find(|child| {
12647                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
12648                        })
12649                    });
12650                    let levels = match declarator {
12651                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
12652                        None => Vec::new(),
12653                    };
12654                    work.push(Work::ApplyLevels(levels));
12655                    work.push(Work::Visit {
12656                        node: inner_type,
12657                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
12658                    });
12659                }
12660                "sized_type_specifier" => {
12661                    // `unsigned char` is one primitive type whose components are
12662                    // partly unnamed tokens, so the whole specifier is its own
12663                    // name component. Reducing it to the `type` child would make
12664                    // `f(unsigned char)` and `f(char)` compare equal.
12665                    let name = StructuredTypeName::new(
12666                        vec![normalize_cpp_whitespace(node_text(node, source))],
12667                        lexical_scope.to_vec(),
12668                        false,
12669                    )?;
12670                    values.push(cpp_push_comparable_node(
12671                        &mut nodes,
12672                        CppComparableNode::Named {
12673                            name,
12674                            primitive: true,
12675                            konst: qualifiers.konst,
12676                            volatil: qualifiers.volatil,
12677                        },
12678                    ));
12679                }
12680                "qualified_identifier"
12681                | "scoped_identifier"
12682                | "scoped_type_identifier"
12683                | "type_identifier"
12684                | "field_identifier"
12685                | "identifier"
12686                | "namespace_identifier"
12687                | "primitive_type"
12688                | "template_type" => {
12689                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
12690                    values.push(cpp_push_comparable_node(
12691                        &mut nodes,
12692                        CppComparableNode::Named {
12693                            name,
12694                            primitive: node.kind() == "primitive_type",
12695                            konst: qualifiers.konst,
12696                            volatil: qualifiers.volatil,
12697                        },
12698                    ));
12699                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
12700                        let mut cursor = arguments_node.walk();
12701                        let arguments = arguments_node
12702                            .named_children(&mut cursor)
12703                            .filter(|child| !child.is_extra() && child.kind() != "comment")
12704                            .collect::<Vec<_>>();
12705                        work.push(Work::BuildGeneric {
12706                            argument_count: arguments.len(),
12707                        });
12708                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
12709                            node: argument,
12710                            qualifiers: CppCvQualifiers::default(),
12711                        }));
12712                    }
12713                }
12714                _ => {
12715                    let inner = node.child_by_field_name("type").or_else(|| {
12716                        (node.named_child_count() == 1)
12717                            .then(|| node.named_child(0))
12718                            .flatten()
12719                    })?;
12720                    work.push(Work::Visit {
12721                        node: inner,
12722                        qualifiers,
12723                    });
12724                }
12725            },
12726            Work::ApplyLevels(levels) => {
12727                let mut root = values.pop()?;
12728                for level in levels {
12729                    let node = match level {
12730                        CppComparableLevel::Pointer { konst, volatil } => {
12731                            CppComparableNode::Pointer {
12732                                inner: root,
12733                                konst,
12734                                volatil,
12735                            }
12736                        }
12737                        CppComparableLevel::Reference => {
12738                            CppComparableNode::Reference { inner: root }
12739                        }
12740                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
12741                    };
12742                    root = cpp_push_comparable_node(&mut nodes, node);
12743                }
12744                values.push(root);
12745            }
12746            Work::BuildGeneric { argument_count } => {
12747                let value_count = argument_count.checked_add(1)?;
12748                let start = values.len().checked_sub(value_count)?;
12749                let mut built = values.split_off(start);
12750                let base = built.remove(0);
12751                values.push(cpp_push_comparable_node(
12752                    &mut nodes,
12753                    CppComparableNode::Generic {
12754                        base,
12755                        arguments: built,
12756                    },
12757                ));
12758            }
12759        }
12760    }
12761    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
12762    debug_assert_eq!(
12763        root,
12764        nodes.len().saturating_sub(1),
12765        "comparable nodes are appended in post-order, so the root is the last one"
12766    );
12767    Some(CppComparableParameter { nodes, root })
12768}
12769
12770fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
12771    nodes.push(node);
12772    nodes.len() - 1
12773}
12774
12775/// The template argument list of the name `node` terminates in, if any.
12776///
12777/// `std::vector<int>` writes its arguments on the `name` of a qualified
12778/// identifier, so a walk that stopped at the qualified node would reduce
12779/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
12780fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
12781    let mut current = node;
12782    loop {
12783        match current.kind() {
12784            "template_type" => return current.child_by_field_name("arguments"),
12785            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12786                current = current.child_by_field_name("name")?;
12787            }
12788            _ => return None,
12789        }
12790    }
12791}
12792
12793/// The callable declarator of the declaration that covers `start_byte`.
12794///
12795/// A consumer that holds a declaration's recorded byte position rather than its
12796/// syntax node - external header extraction, for instance - uses this to reach
12797/// the same `function_declarator` the declaration walk read.
12798pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
12799    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
12800    loop {
12801        if matches!(
12802            current.kind(),
12803            "declaration" | "field_declaration" | "function_definition"
12804        ) && let Some(declarator) = current
12805            .child_by_field_name("declarator")
12806            .and_then(extract_function_declarator)
12807        {
12808            return Some(declarator);
12809        }
12810        current = current.parent()?;
12811    }
12812}
12813
12814fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
12815    let mut labels = Vec::new();
12816    let mut cursor = parameters_node.walk();
12817    for child in parameters_node.children(&mut cursor) {
12818        match child.kind() {
12819            "parameter_declaration" | "optional_parameter_declaration" => {
12820                if let Some(name_node) = child
12821                    .child_by_field_name("declarator")
12822                    .and_then(cpp_declarator_label_node)
12823                {
12824                    labels.push(name_node);
12825                } else {
12826                    labels.push(child);
12827                }
12828            }
12829            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12830                labels.push(child);
12831            }
12832            _ => {}
12833        }
12834    }
12835    labels
12836}
12837
12838fn cpp_signature_search_start<'tree>(
12839    signature: &str,
12840    function_declarator: Node<'tree>,
12841    source: &str,
12842    ancestry: &ParentIndex<'tree>,
12843) -> usize {
12844    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
12845        return 0;
12846    };
12847    let raw = node_text(enclosing, source);
12848    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
12849    let offset = function_declarator
12850        .start_byte()
12851        .saturating_sub(enclosing.start_byte())
12852        .saturating_sub(leading_trim_bytes);
12853    offset.min(signature.len())
12854}
12855
12856fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
12857    match node.kind() {
12858        "identifier" | "field_identifier" => Some(node),
12859        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
12860            .child_by_field_name("declarator")
12861            .or_else(|| last_named_child(node))
12862            .and_then(cpp_declarator_label_node),
12863        "array_declarator" => node
12864            .child_by_field_name("declarator")
12865            .and_then(cpp_declarator_label_node),
12866        "function_declarator" => node
12867            .child_by_field_name("declarator")
12868            .or_else(|| node.child_by_field_name("name"))
12869            .or_else(|| last_named_child(node))
12870            .and_then(cpp_declarator_label_node),
12871        _ => None,
12872    }
12873}
12874
12875fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
12876    let base_type = parameter
12877        .child_by_field_name("type")
12878        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
12879        .unwrap_or_default();
12880    let declarator = cpp_parameter_declarator(parameter);
12881    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
12882    // are discarded, so `f(const int)` and `f(int)` declare one function. A
12883    // qualifier written next to the parameter's type is only top-level when
12884    // the declarator adds no indirection; behind a pointer, reference or array
12885    // declarator the same qualifier belongs to the pointee, referent or
12886    // element and keeps distinguishing the type (#1827).
12887    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
12888    let mut cursor = parameter.walk();
12889    let qualifiers = parameter
12890        .named_children(&mut cursor)
12891        .filter(|child| child.kind() == "type_qualifier")
12892        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12893        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
12894        .collect::<Vec<_>>()
12895        .join(" ");
12896    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
12897        (true, _) => base_type,
12898        (_, true) => qualifiers,
12899        (false, false) => format!("{qualifiers} {base_type}"),
12900    };
12901    let declarator_suffix = declarator
12902        .map(|node| cpp_declarator_suffix_without_name(node, source))
12903        .unwrap_or_default();
12904
12905    let combined = if type_text.is_empty() {
12906        declarator_suffix
12907    } else if declarator_suffix.is_empty() {
12908        type_text
12909    } else {
12910        format!("{type_text} {declarator_suffix}")
12911    };
12912    normalize_cpp_type_text(&combined)
12913}
12914
12915fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
12916    parameter.child_by_field_name("declarator").or_else(|| {
12917        // Some unnamed prototype parameters expose their abstract declarator
12918        // as a direct named child without the grammar's `declarator` field.
12919        // Recover only the structured abstract-declarator node; the parameter's
12920        // type and qualifiers are distinct children and must not be guessed from
12921        // source text.
12922        let mut cursor = parameter.walk();
12923        parameter
12924            .named_children(&mut cursor)
12925            .find(|child| is_cpp_abstract_declarator(child.kind()))
12926    })
12927}
12928
12929/// Whether a parameter's declarator chain adds indirection - a pointer,
12930/// reference, array or function declarator - to the parameter's written type.
12931pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
12932    let mut current = Some(declarator);
12933    while let Some(node) = current {
12934        if matches!(
12935            node.kind(),
12936            "pointer_declarator"
12937                | "abstract_pointer_declarator"
12938                | "reference_declarator"
12939                | "abstract_reference_declarator"
12940                | "array_declarator"
12941                | "abstract_array_declarator"
12942                | "function_declarator"
12943                | "abstract_function_declarator"
12944        ) {
12945            return true;
12946        }
12947        current = cpp_nested_declarator(node);
12948    }
12949    false
12950}
12951
12952fn is_cpp_abstract_declarator(kind: &str) -> bool {
12953    matches!(
12954        kind,
12955        "abstract_pointer_declarator"
12956            | "abstract_reference_declarator"
12957            | "abstract_array_declarator"
12958            | "abstract_function_declarator"
12959            | "abstract_parenthesized_declarator"
12960    )
12961}
12962
12963fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
12964    node.child_by_field_name("declarator").or_else(|| {
12965        if is_cpp_abstract_declarator(node.kind()) {
12966            let mut cursor = node.walk();
12967            node.named_children(&mut cursor)
12968                .find(|child| is_cpp_abstract_declarator(child.kind()))
12969        } else {
12970            // Named declarators historically use their last named child when
12971            // tree-sitter omits the field. Keep that broad fallback for
12972            // attributed, variadic, and recovered named shapes.
12973            last_named_child(node)
12974        }
12975    })
12976}
12977
12978fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
12979    match node.kind() {
12980        "identifier" | "field_identifier" => String::new(),
12981        "pointer_declarator" | "abstract_pointer_declarator" => {
12982            let inner = cpp_nested_declarator(node)
12983                .map(|child| cpp_declarator_suffix_without_name(child, source))
12984                .unwrap_or_default();
12985            format!("*{inner}")
12986        }
12987        "reference_declarator" | "abstract_reference_declarator" => {
12988            let inner = cpp_nested_declarator(node)
12989                .map(|child| cpp_declarator_suffix_without_name(child, source))
12990                .unwrap_or_default();
12991            let reference = node
12992                .children(&mut node.walk())
12993                .find(|child| matches!(child.kind(), "&" | "&&"))
12994                .map(|child| node_text(child, source))
12995                .unwrap_or("&");
12996            format!("{reference}{inner}")
12997        }
12998        "array_declarator" | "abstract_array_declarator" => {
12999            let inner = cpp_nested_declarator(node)
13000                .map(|child| cpp_declarator_suffix_without_name(child, source))
13001                .unwrap_or_default();
13002            let size = node
13003                .child_by_field_name("size")
13004                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13005                .unwrap_or_default();
13006            format!("{inner}[{size}]")
13007        }
13008        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13009            let inner = cpp_nested_declarator(node);
13010            inner
13011                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13012                .unwrap_or_default()
13013        }
13014        "function_declarator" | "abstract_function_declarator" => {
13015            let inner = cpp_nested_declarator(node)
13016                .map(|child| cpp_declarator_suffix_without_name(child, source))
13017                .unwrap_or_default();
13018            let params = node
13019                .child_by_field_name("parameters")
13020                .map(|child| cpp_parameter_signature(child, source))
13021                .unwrap_or_else(|| "()".to_string());
13022            format!("{inner}{params}")
13023        }
13024        _ => {
13025            let text = normalize_cpp_whitespace(node_text(node, source));
13026            let name = extract_declarator_name(node, source);
13027            if name.is_empty() {
13028                text
13029            } else {
13030                text.replace(&name, "").trim().to_string()
13031            }
13032        }
13033    }
13034}
13035
13036fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13037    collapse_cpp_whitespace(
13038        suffix
13039            .trim()
13040            .trim_start_matches("->")
13041            .trim_start_matches('{')
13042            .trim_end_matches(';'),
13043    )
13044}
13045
13046pub fn normalize_cpp_whitespace(value: &str) -> String {
13047    collapse_cpp_whitespace(value)
13048}
13049
13050fn normalize_cpp_type_text(value: &str) -> String {
13051    collapse_cpp_whitespace(value)
13052        .replace(", ", ",")
13053        .replace(" <", "<")
13054        .replace("< ", "<")
13055        .replace(" >", ">")
13056}
13057
13058fn collapse_cpp_whitespace(value: &str) -> String {
13059    let mut result = String::new();
13060    let mut prev_space = false;
13061    for ch in value.chars() {
13062        if ch.is_whitespace() {
13063            if !prev_space {
13064                result.push(' ');
13065            }
13066            prev_space = true;
13067        } else {
13068            result.push(ch);
13069            prev_space = false;
13070        }
13071    }
13072    result.trim().to_string()
13073}
13074
13075pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13076    node_source_text(node, source)
13077}
13078
13079pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13080    walk_named_tree_preorder(node, true, |node| {
13081        match node.kind() {
13082            "type_identifier" | "identifier" | "qualified_identifier" => {
13083                let text = node_text(node, source).trim();
13084                if !text.is_empty() {
13085                    identifiers.insert(text.to_string());
13086                }
13087            }
13088            _ => {}
13089        }
13090        WalkControl::Continue
13091    });
13092}
13093
13094fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13095    node.child_by_field_name("body").or_else(|| {
13096        let mut cursor = node.walk();
13097        node.named_children(&mut cursor).find(|child| {
13098            matches!(
13099                child.kind(),
13100                "declaration_list" | "field_declaration_list" | "enumerator_list"
13101            )
13102        })
13103    })
13104}
13105
13106/// Return a class body's actual closing brace when the parser supplied one.
13107///
13108/// A malformed namespace sentinel can leave a class node carrying unrelated
13109/// parser errors even though its own class body is complete.  `has_error()` is
13110/// therefore too coarse an admission predicate for sentinel ownership.  The
13111/// body list, however, exposes the opening and closing punctuation directly;
13112/// a real (non-missing) final `}` proves that the class did not borrow the
13113/// enclosing namespace's close.  Requiring the body to end before its parent
13114/// container also rejects a recovered node whose body swallowed that outer
13115/// boundary.
13116fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13117    if !matches!(
13118        node.kind(),
13119        "class_specifier" | "struct_specifier" | "union_specifier"
13120    ) {
13121        return None;
13122    }
13123    let body = cpp_body_node(node)?;
13124    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13125        return None;
13126    }
13127    let open = body.child(0)?;
13128    let close = body.child(body.child_count().checked_sub(1)?)?;
13129    if open.kind() != "{"
13130        || open.is_missing()
13131        || close.kind() != "}"
13132        || close.is_missing()
13133        || close.end_byte() != body.end_byte()
13134        || body.end_byte() > node.end_byte()
13135        || node
13136            .parent()
13137            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13138    {
13139        return None;
13140    }
13141    Some(close)
13142}
13143
13144fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13145    if node.kind() == "namespace_definition" {
13146        return true;
13147    }
13148    let mut cursor = node.walk();
13149    node.named_children(&mut cursor)
13150        .any(cpp_contains_namespace_definition)
13151}
13152
13153struct CppNestedNamespaceSentinel<'tree> {
13154    function: Node<'tree>,
13155    body: Node<'tree>,
13156    namespace_components: Vec<String>,
13157}
13158
13159/// Owned structural recovery metadata for a namespace-sentinel region.
13160///
13161/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
13162/// instead of the namespace/class scopes that the declaration visitor restores.
13163/// The inverted usage walk has the original CST, so it needs the same ownership
13164/// evidence without borrowing parser nodes across its file scan.  Keep this
13165/// descriptor deliberately source-range based: callers can match a reference
13166/// node by containment and then resolve its structured type spelling in the
13167/// recovered class scope.
13168#[derive(Debug, Clone)]
13169pub struct CppSentinelRecoveredOwner {
13170    pub range: Range,
13171    /// Start of the qualified owner name (`btree<P>::method`).  A leading
13172    /// return type before this byte is looked up from the namespace; parameters,
13173    /// trailing returns, and the body use the member owner scope.
13174    pub owner_name_start_byte: usize,
13175    /// Number of leading components belonging to the namespace rather than
13176    /// the qualified class owner.  A leading return type is looked up before
13177    /// every owner component, not merely before the innermost class.
13178    pub namespace_component_count: usize,
13179    pub scope_components: Vec<String>,
13180}
13181
13182#[derive(Debug, Clone)]
13183pub struct CppSentinelRecoveredClass {
13184    pub namespace_range: Range,
13185    pub namespace_scope_components: Vec<String>,
13186    pub class_range: Range,
13187    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
13188    pub scope_components: Vec<String>,
13189    /// Qualified out-of-line member definitions owned by this class.  Their
13190    /// ranges may extend beyond `class_range` when the malformed sentinel
13191    /// swallowed the namespace close and left definitions as function siblings.
13192    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13193}
13194
13195/// Resolve the lexical scope restored for a node in a malformed
13196/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
13197/// outrank class spans, which in turn outrank the surviving namespace body.
13198/// The class ancestor suffix is recovered from the original CST so nested
13199/// members keep their complete `Outer::Inner` owner chain.
13200pub fn cpp_sentinel_recovered_scope_for_node(
13201    node: Node<'_>,
13202    source: &str,
13203    recovered_classes: &[CppSentinelRecoveredClass],
13204) -> Option<Vec<String>> {
13205    let contains =
13206        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13207    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13208    for recovered in recovered_classes {
13209        for owner in recovered
13210            .owner_ranges
13211            .iter()
13212            .filter(|owner| contains(owner.range))
13213        {
13214            let replace = best_owner.is_none_or(|existing| {
13215                owner.range.end_byte.saturating_sub(owner.range.start_byte)
13216                    < existing
13217                        .range
13218                        .end_byte
13219                        .saturating_sub(existing.range.start_byte)
13220            });
13221            if replace {
13222                best_owner = Some(owner);
13223            }
13224        }
13225    }
13226    if let Some(owner) = best_owner {
13227        let mut scope = owner.scope_components.clone();
13228        if node.start_byte() < owner.owner_name_start_byte {
13229            scope.truncate(owner.namespace_component_count);
13230        }
13231        return Some(scope);
13232    }
13233
13234    let class = recovered_classes
13235        .iter()
13236        .filter(|recovered| contains(recovered.class_range))
13237        .min_by_key(|recovered| {
13238            recovered
13239                .class_range
13240                .end_byte
13241                .saturating_sub(recovered.class_range.start_byte)
13242        });
13243    let class_scope = class.is_some();
13244    let mut scope = if let Some(class) = class {
13245        class.scope_components.clone()
13246    } else {
13247        let namespace = recovered_classes
13248            .iter()
13249            .filter(|recovered| contains(recovered.namespace_range))
13250            .min_by_key(|recovered| {
13251                recovered
13252                    .namespace_range
13253                    .end_byte
13254                    .saturating_sub(recovered.namespace_range.start_byte)
13255            })?;
13256        let mut scope = namespace.namespace_scope_components.clone();
13257        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13258        let common_prefix = scope
13259            .iter()
13260            .zip(&parser_namespace)
13261            .take_while(|(recovered, parser)| recovered == parser)
13262            .count();
13263        scope.extend(parser_namespace.into_iter().skip(common_prefix));
13264        scope
13265    };
13266    if class_scope {
13267        let mut ancestor_components = Vec::new();
13268        let mut ancestor = node.parent();
13269        while let Some(current) = ancestor {
13270            if matches!(
13271                current.kind(),
13272                "class_specifier" | "struct_specifier" | "union_specifier"
13273            ) && let Some(name) = current.child_by_field_name("name")
13274                && let Some(name_components) = cpp_name_components(name, source)
13275            {
13276                ancestor_components.push(
13277                    name_components
13278                        .into_iter()
13279                        .map(|component| component.name)
13280                        .collect::<Vec<_>>(),
13281                );
13282            }
13283            ancestor = current.parent();
13284        }
13285        ancestor_components.reverse();
13286        let base_len = scope.len();
13287        for component in ancestor_components.into_iter().flatten() {
13288            if scope.len() >= base_len && scope.last() == Some(&component) {
13289                continue;
13290            }
13291            scope.push(component);
13292        }
13293    }
13294    Some(scope)
13295}
13296
13297struct CppSentinelFragmentedClassTail<'tree> {
13298    class_node: Node<'tree>,
13299    template_node: Option<Node<'tree>>,
13300    name: String,
13301    raw_supertypes: Option<Vec<String>>,
13302    fragmented: FragmentedExportBody,
13303    consumed_start: usize,
13304}
13305
13306struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13307    name: String,
13308    open: Node<'tree>,
13309    raw_supertypes: Option<Vec<String>>,
13310}
13311
13312struct CppSentinelDirectBodyClassRegion {
13313    namespace_components: Vec<String>,
13314    class_start: usize,
13315    class_start_line: usize,
13316    class_close_end: usize,
13317    class_close_line: usize,
13318    name: String,
13319}
13320
13321fn cpp_sentinel_body_class_candidate<'tree>(
13322    child: Node<'tree>,
13323) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13324    if matches!(
13325        child.kind(),
13326        "class_specifier" | "struct_specifier" | "union_specifier"
13327    ) {
13328        return Some((child, None));
13329    }
13330    if child.kind() != "template_declaration" {
13331        if child.kind() == "declaration" {
13332            return Some((first_class_like_child(child)?, None));
13333        }
13334        return None;
13335    }
13336    let mut cursor = child.walk();
13337    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13338        if matches!(
13339            candidate.kind(),
13340            "class_specifier" | "struct_specifier" | "union_specifier"
13341        ) {
13342            Some(candidate)
13343        } else if candidate.kind() == "declaration" {
13344            first_class_like_child(candidate)
13345        } else {
13346            None
13347        }
13348    })?;
13349    Some((class_node, Some(child)))
13350}
13351
13352/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
13353/// namespace-sentinel body when a later member macro ends the bogus sentinel
13354/// function before the real class close. The anonymous class/open tokens and
13355/// direct identifier are the structural proof; a retained direct close would
13356/// be an ordinary malformed class rather than the fragmented tail handled here.
13357fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13358    node: Node<'tree>,
13359    source: &str,
13360) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13361    let name = malformed_class_error_owner_name(node, source)?;
13362    let mut cursor = node.walk();
13363    let children = node.children(&mut cursor).collect::<Vec<_>>();
13364    let keyword = children.first()?;
13365    let open_index = children.iter().position(|child| child.kind() == "{")?;
13366    if children[open_index + 1..]
13367        .iter()
13368        .any(|child| child.kind() == "}")
13369    {
13370        return None;
13371    }
13372    let raw_supertypes =
13373        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13374    Some(CppSentinelFragmentedClassErrorPrefix {
13375        name,
13376        open: children[open_index],
13377        raw_supertypes,
13378    })
13379}
13380
13381fn cpp_sentinel_direct_body_class_candidate<'tree>(
13382    child: Node<'tree>,
13383) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13384    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13385        return Some(candidate);
13386    }
13387    if child.kind() != "template_declaration" {
13388        return None;
13389    }
13390    let mut cursor = child.walk();
13391    let wrapper = child
13392        .named_children(&mut cursor)
13393        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13394    Some((first_class_like_child(wrapper)?, Some(child)))
13395}
13396
13397fn cpp_sentinel_direct_namespace_components(
13398    function: Node<'_>,
13399    body: Node<'_>,
13400    source: &str,
13401) -> Option<Vec<String>> {
13402    let mut cursor = function.walk();
13403    let children = function
13404        .named_children(&mut cursor)
13405        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13406        .collect::<Vec<_>>();
13407    let sentinel_index = children.iter().rposition(|child| {
13408        direct_identifier_name(*child, source)
13409            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
13410    })?;
13411    let mut identifiers = Vec::new();
13412    let mut stack = children[sentinel_index + 1..]
13413        .iter()
13414        .rev()
13415        .copied()
13416        .collect::<Vec<_>>();
13417    while let Some(current) = stack.pop() {
13418        if let Some(name) = direct_identifier_name(current, source) {
13419            identifiers.push(name);
13420            continue;
13421        }
13422        let mut cursor = current.walk();
13423        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
13424        stack.extend(children.into_iter().rev());
13425    }
13426    let [keyword, namespace] = identifiers.as_slice() else {
13427        return None;
13428    };
13429    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
13430        .then(|| vec![namespace.clone()])
13431}
13432
13433fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
13434    let mut sibling = class_semicolon.next_named_sibling();
13435    let namespace_close = loop {
13436        let Some(current) = sibling else {
13437            return false;
13438        };
13439        sibling = current.next_named_sibling();
13440        if current.kind() != "comment" {
13441            break current;
13442        }
13443    };
13444    if !cpp_is_stray_close_brace(namespace_close, source) {
13445        return false;
13446    }
13447    loop {
13448        let Some(current) = sibling else {
13449            return false;
13450        };
13451        sibling = current.next_named_sibling();
13452        if current.kind() == "comment" {
13453            continue;
13454        }
13455        return direct_identifier_name(current, source)
13456            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
13457    }
13458}
13459
13460fn cpp_sentinel_macro_body_class_region<'tree>(
13461    node: Node<'tree>,
13462    source: &str,
13463    ancestry: &ParentIndex<'tree>,
13464) -> Option<CppSentinelDirectBodyClassRegion> {
13465    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
13466        return None;
13467    };
13468    if node.kind() != "function_definition" || !node.has_error() {
13469        return None;
13470    }
13471    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
13472    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
13473    let mut cursor = body.walk();
13474    let candidates = body
13475        .named_children(&mut cursor)
13476        .filter_map(cpp_sentinel_direct_body_class_candidate)
13477        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
13478        .collect::<Vec<_>>();
13479    let [(class_node, template_node)] = candidates.as_slice() else {
13480        return None;
13481    };
13482    let original_body = cpp_body_node(*class_node)?;
13483    let name = class_like_name(*class_node, source, ancestry)?;
13484    if name.is_empty() || cpp_export_macro_token(&name) {
13485        return None;
13486    }
13487
13488    let mut sibling = node.next_named_sibling();
13489    let (class_close_start, class_close_end, class_close_line) = loop {
13490        let current = sibling?;
13491        let next = current.next_named_sibling();
13492        if cpp_is_stray_close_brace(current, source)
13493            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
13494        {
13495            let semicolon = next.expect("checked above");
13496            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
13497                return None;
13498            }
13499            break (
13500                current.start_byte(),
13501                semicolon.end_byte(),
13502                semicolon.end_position().row + 1,
13503            );
13504        }
13505        sibling = next;
13506    };
13507    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
13508    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
13509    let root = tree.root_node();
13510    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
13511    // The region reparse is its own tree, so it needs its own parent index;
13512    // the caller's index answers nothing about these nodes.
13513    let reparsed_ancestry = ParentIndex::new(root);
13514    let reparsed =
13515        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
13516    if reparsed.name != name
13517        || reparsed.declaration_node.start_byte() != class_node.start_byte()
13518        || reparsed.body.start_byte() != original_body.start_byte()
13519        || class_close_start <= reparsed.body.end_byte()
13520        || class_close_end <= class_node.end_byte()
13521    {
13522        return None;
13523    }
13524    Some(CppSentinelDirectBodyClassRegion {
13525        namespace_components,
13526        class_start: reparse_start,
13527        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
13528            node.start_position().row + 1
13529        }),
13530        class_close_end,
13531        class_close_line,
13532        name,
13533    })
13534}
13535
13536/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
13537/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
13538///
13539/// The parser puts the namespace opener and the malformed function in one root
13540/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
13541/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
13542/// the malformed function must begin with an all-caps type, then an ERROR whose
13543/// sole identifier is `namespace`, followed by the inner namespace identifier
13544/// and a compound body; and that body must contain a complete named class or a
13545/// structurally fragmented class prefix. A text reparse cannot prove any of
13546/// those ownership boundaries.
13547fn cpp_nested_namespace_sentinel<'tree>(
13548    node: Node<'tree>,
13549    source: &str,
13550    ancestry: &ParentIndex<'tree>,
13551) -> Option<CppNestedNamespaceSentinel<'tree>> {
13552    if !node.has_error() {
13553        return None;
13554    }
13555
13556    let (function, mut namespace_components) = if node.kind() == "ERROR" {
13557        let mut cursor = node.walk();
13558        let functions = node
13559            .named_children(&mut cursor)
13560            .filter(|child| child.kind() == "function_definition")
13561            .collect::<Vec<_>>();
13562        let [function] = functions.as_slice() else {
13563            return None;
13564        };
13565        if !function.has_error() {
13566            return None;
13567        }
13568        let mut cursor = node.walk();
13569        let children = node.children(&mut cursor).collect::<Vec<_>>();
13570        let function_index = children
13571            .iter()
13572            .position(|child| same_node(*child, *function))?;
13573        let [outer_keyword, outer_name, outer_open] =
13574            children.get(function_index.checked_sub(3)?..function_index)?
13575        else {
13576            return None;
13577        };
13578        if outer_keyword.kind() != "namespace"
13579            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
13580            || outer_open.kind() != "{"
13581        {
13582            return None;
13583        }
13584        (
13585            *function,
13586            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
13587        )
13588    } else if node.kind() == "function_definition" {
13589        let declaration_list = node.parent()?;
13590        let namespace = declaration_list.parent()?;
13591        if declaration_list.kind() != "declaration_list"
13592            || namespace.kind() != "namespace_definition"
13593            || namespace.child_by_field_name("body") != Some(declaration_list)
13594        {
13595            return None;
13596        }
13597        (node, Vec::new())
13598    } else {
13599        return None;
13600    };
13601
13602    let mut cursor = function.walk();
13603    let named = function
13604        .named_children(&mut cursor)
13605        .filter(|child| child.kind() != "comment")
13606        .collect::<Vec<_>>();
13607    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
13608        return None;
13609    };
13610    if first_type.kind() != "type_identifier" {
13611        return None;
13612    }
13613    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
13614    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
13615        return None;
13616    }
13617    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
13618        return None;
13619    }
13620    let inner_keyword = inner_error.named_child(0)?;
13621    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
13622        return None;
13623    }
13624    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
13625        return None;
13626    }
13627    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
13628    if inner_name.is_empty() || body.kind() != "compound_statement" {
13629        return None;
13630    }
13631    namespace_components.push(inner_name);
13632
13633    let mut cursor = body.walk();
13634    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13635        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13636            cpp_body_node(class_node).is_some()
13637                && class_like_name(class_node, source, ancestry)
13638                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13639        })
13640    });
13641    if !has_complete_class
13642        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
13643    {
13644        return None;
13645    }
13646
13647    Some(CppNestedNamespaceSentinel {
13648        function,
13649        body: *body,
13650        namespace_components,
13651    })
13652}
13653
13654/// Recognize a namespace-begin sentinel directly beneath the translation unit.
13655///
13656/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
13657/// function whose type is the sentinel, whose declarator is the structured
13658/// qualified name `namespace::a::b`, and whose body contains the namespace
13659/// items. Declaration indexing already reparses this bounded region. The
13660/// inverse scanner retains the original tree, so recover the same namespace
13661/// components from the declarator fields for its lexical-scope metadata.
13662fn cpp_root_namespace_sentinel<'tree>(
13663    node: Node<'tree>,
13664    source: &str,
13665    ancestry: &ParentIndex<'tree>,
13666) -> Option<CppNestedNamespaceSentinel<'tree>> {
13667    if node.kind() != "function_definition"
13668        || !node.has_error()
13669        || node.parent()?.kind() != "translation_unit"
13670    {
13671        return None;
13672    }
13673    let first_type = node.child_by_field_name("type")?;
13674    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
13675    if first_type.kind() != "type_identifier"
13676        || sentinel.is_empty()
13677        || !cpp_export_macro_token(&sentinel)
13678    {
13679        return None;
13680    }
13681    let declarator = node.child_by_field_name("declarator")?;
13682    let body = node.child_by_field_name("body")?;
13683    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
13684        return None;
13685    }
13686    let mut cursor = node.walk();
13687    let named = node
13688        .named_children(&mut cursor)
13689        .filter(|child| child.kind() != "comment")
13690        .collect::<Vec<_>>();
13691    let [named_type, named_declarator, named_body] = named.as_slice() else {
13692        return None;
13693    };
13694    if !same_node(*named_type, first_type)
13695        || !same_node(*named_declarator, declarator)
13696        || !same_node(*named_body, body)
13697    {
13698        return None;
13699    }
13700    let mut declarator_components = Vec::new();
13701    let mut valid_components = true;
13702    walk_named_tree_preorder(declarator, true, |component| {
13703        if !matches!(
13704            component.kind(),
13705            "identifier" | "namespace_identifier" | "type_identifier"
13706        ) {
13707            return WalkControl::Continue;
13708        }
13709        let Some(component) = canonical_cpp_qualified_component(component, source) else {
13710            valid_components = false;
13711            return WalkControl::Break;
13712        };
13713        declarator_components.push(component.name);
13714        WalkControl::SkipChildren
13715    });
13716    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
13717        return None;
13718    }
13719    declarator_components.remove(0);
13720    let namespace_components = declarator_components;
13721    if namespace_components.is_empty()
13722        || namespace_components
13723            .iter()
13724            .any(|component| component.is_empty() || cpp_export_macro_token(component))
13725    {
13726        return None;
13727    }
13728
13729    let mut cursor = body.walk();
13730    let has_complete_class = body.named_children(&mut cursor).any(|child| {
13731        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
13732            cpp_body_node(class_node).is_some()
13733                && class_like_name(class_node, source, ancestry)
13734                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
13735        })
13736    });
13737    if !has_complete_class
13738        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
13739    {
13740        return None;
13741    }
13742
13743    Some(CppNestedNamespaceSentinel {
13744        function: node,
13745        body,
13746        namespace_components,
13747    })
13748}
13749
13750/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
13751/// malformed namespace-sentinel function.  The recovery is deliberately
13752/// structural: the class must be a direct body item, its own class node must be
13753/// erroneous and end before a unique anonymous `}` in the enclosing
13754/// declaration-list, and that namespace's next sibling must be a standalone
13755/// `;`.  The complete interior must pass the existing member-shaped reparse
13756/// gate. This avoids source brace scans and does not borrow a close from an
13757/// unrelated later declaration.
13758fn cpp_sentinel_fragmented_class_tail<'tree>(
13759    function: Node<'tree>,
13760    body: Node<'tree>,
13761    source: &str,
13762    ancestry: &ParentIndex<'tree>,
13763) -> Option<CppSentinelFragmentedClassTail<'tree>> {
13764    let mut cursor = body.walk();
13765    let candidates = body
13766        .named_children(&mut cursor)
13767        .filter_map(|child| {
13768            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
13769                let class_body = cpp_body_node(class_node)?;
13770                if !class_node.has_error() {
13771                    return None;
13772                }
13773                let name = class_like_name(class_node, source, ancestry)?;
13774                let raw_supertypes =
13775                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
13776                        .then(|| extract_cpp_supertypes(class_node, source));
13777                return Some((
13778                    class_node,
13779                    template_node,
13780                    name,
13781                    class_body,
13782                    class_body.start_byte().checked_add(1)?,
13783                    raw_supertypes,
13784                ));
13785            }
13786            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
13787            Some((
13788                child,
13789                None,
13790                prefix.name,
13791                prefix.open,
13792                prefix.open.end_byte(),
13793                prefix.raw_supertypes,
13794            ))
13795        })
13796        .collect::<Vec<_>>();
13797    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
13798        candidates.as_slice()
13799    else {
13800        return None;
13801    };
13802    if name.is_empty() || cpp_export_macro_token(name) {
13803        return None;
13804    }
13805
13806    let (close, semicolon) =
13807        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
13808
13809    let reparse_end = close.start_byte();
13810    if *reparse_start >= reparse_end {
13811        return None;
13812    }
13813    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
13814    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
13815        return None;
13816    }
13817    let class_range = Range {
13818        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13819        end_byte: semicolon.end_byte(),
13820        start_line: template_node.map_or(class_node.start_position().row, |node| {
13821            node.start_position().row
13822        }) + 1,
13823        end_line: semicolon.end_position().row + 1,
13824    };
13825    Some(CppSentinelFragmentedClassTail {
13826        class_node: *class_node,
13827        template_node: *template_node,
13828        name: name.clone(),
13829        raw_supertypes: raw_supertypes.clone(),
13830        fragmented: FragmentedExportBody {
13831            reparse_start: *reparse_start,
13832            reparse_end,
13833            class_range,
13834        },
13835        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
13836    })
13837}
13838
13839/// Recover the class and out-of-line owner scopes from every malformed
13840/// namespace-sentinel region in `root`.
13841///
13842/// This is the shared structural counterpart to
13843/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
13844/// reuses the visitor's sentinel/class admission predicates instead of parsing
13845/// source text a second time.  The returned values own only ranges and names, so
13846/// they can be retained by an inverted usage scan after the tree borrow ends.
13847pub fn cpp_sentinel_recovered_classes(
13848    root: Node<'_>,
13849    source: &str,
13850) -> Vec<CppSentinelRecoveredClass> {
13851    if !root.has_error() {
13852        return Vec::new();
13853    }
13854    // This scan owns its walk of `root`, so it owns the parent index that walk
13855    // asks its ancestor questions through. Built after the error gate: a clean
13856    // tree returns without paying for one.
13857    let ancestry = ParentIndex::new(root);
13858    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
13859    let mut stack = vec![root];
13860    while let Some(current) = stack.pop() {
13861        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
13862            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
13863        {
13864            let namespace_components = cpp_sentinel_recovered_namespace_components(
13865                recovered.function,
13866                &recovered.namespace_components,
13867                source,
13868            );
13869            let fragmented = cpp_sentinel_fragmented_class_tail(
13870                recovered.function,
13871                recovered.body,
13872                source,
13873                &ancestry,
13874            );
13875            let mut class_candidates = Vec::new();
13876            let mut cursor = recovered.body.walk();
13877            for (class_node, template_node) in recovered
13878                .body
13879                .named_children(&mut cursor)
13880                .filter_map(cpp_sentinel_body_class_candidate)
13881            {
13882                let Some(name) = class_like_name(class_node, source, &ancestry) else {
13883                    continue;
13884                };
13885                if name.is_empty() || cpp_export_macro_token(&name) {
13886                    continue;
13887                }
13888                let is_fragmented = fragmented
13889                    .as_ref()
13890                    .is_some_and(|tail| same_node(tail.class_node, class_node));
13891                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
13892                    continue;
13893                }
13894                let class_range = if is_fragmented {
13895                    fragmented
13896                        .as_ref()
13897                        .map(|tail| tail.fragmented.class_range)
13898                        .expect("fragmented class range is present when class matches")
13899                } else {
13900                    cpp_declaration_range(template_node.unwrap_or(class_node))
13901                };
13902                class_candidates.push((class_range, name));
13903            }
13904            if let Some(fragmented) = fragmented
13905                .as_ref()
13906                .filter(|tail| tail.class_node.kind() == "ERROR")
13907            {
13908                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
13909            }
13910
13911            let mut owner_ranges =
13912                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
13913            cpp_sentinel_extend_unique_owner_ranges(
13914                &mut owner_ranges,
13915                cpp_sentinel_recovered_sibling_owner_ranges(
13916                    recovered.function,
13917                    &namespace_components,
13918                    source,
13919                ),
13920            );
13921            for (class_range, name) in class_candidates {
13922                push_cpp_sentinel_recovered_class(
13923                    &mut recovered_classes,
13924                    cpp_declaration_range(recovered.body),
13925                    &namespace_components,
13926                    class_range,
13927                    name,
13928                    &owner_ranges,
13929                );
13930            }
13931
13932            if let Some(declaration_list) = recovered
13933                .function
13934                .parent()
13935                .filter(|parent| parent.kind() == "declaration_list")
13936            {
13937                let outer_namespace =
13938                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
13939                push_cpp_sentinel_sibling_classes(
13940                    &mut recovered_classes,
13941                    declaration_list,
13942                    recovered.function,
13943                    &outer_namespace,
13944                    source,
13945                    &ancestry,
13946                );
13947            }
13948        } else if let Some(region) =
13949            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
13950        {
13951            let namespace_components = cpp_sentinel_recovered_namespace_components(
13952                current,
13953                &region.namespace_components,
13954                source,
13955            );
13956            let owner_container = current
13957                .parent()
13958                .filter(|parent| parent.kind() == "declaration_list")
13959                .unwrap_or(current);
13960            let owner_ranges =
13961                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
13962            push_cpp_sentinel_recovered_class(
13963                &mut recovered_classes,
13964                cpp_declaration_range(owner_container),
13965                &namespace_components,
13966                Range {
13967                    start_byte: region.class_start,
13968                    end_byte: region.class_close_end,
13969                    start_line: region.class_start_line,
13970                    end_line: region.class_close_line,
13971                },
13972                region.name,
13973                &owner_ranges,
13974            );
13975        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
13976            // A generic sentinel-prefixed class can be reduced as a malformed
13977            // function/ERROR without the explicit `namespace X` token pair.
13978            // Reuse the declaration visitor's bounded reparse and retain only
13979            // the recovered class identity/range here.
13980            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
13981                region;
13982            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
13983                continue;
13984            };
13985            let root = tree.root_node();
13986            let template_node = cpp_sentinel_reparsed_leading_template(root);
13987            // A region reparse is its own tree and needs its own parent index.
13988            let reparsed_ancestry = ParentIndex::new(root);
13989            let Some(reparsed_class) =
13990                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
13991            else {
13992                continue;
13993            };
13994            let class_node = reparsed_class.declaration_node;
13995            let name = reparsed_class.name;
13996            let namespace_components =
13997                cpp_sentinel_recovered_namespace_components(current, &[], source);
13998            let owner_container = current
13999                .parent()
14000                .filter(|parent| parent.kind() == "declaration_list")
14001                .unwrap_or(current);
14002            let mut owner_ranges =
14003                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14004            cpp_sentinel_extend_unique_owner_ranges(
14005                &mut owner_ranges,
14006                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14007            );
14008            push_cpp_sentinel_recovered_class(
14009                &mut recovered_classes,
14010                cpp_declaration_range(owner_container),
14011                &namespace_components,
14012                Range {
14013                    start_byte: class_start,
14014                    end_byte: close_end,
14015                    start_line: class_node.start_position().row + 1,
14016                    end_line: class_node.end_position().row + 1,
14017                },
14018                name,
14019                &owner_ranges,
14020            );
14021            if owner_container.kind() == "declaration_list" {
14022                push_cpp_sentinel_sibling_classes(
14023                    &mut recovered_classes,
14024                    owner_container,
14025                    current,
14026                    &namespace_components,
14027                    source,
14028                    &ancestry,
14029                );
14030            }
14031        }
14032
14033        let mut cursor = current.walk();
14034        stack.extend(current.named_children(&mut cursor));
14035    }
14036    // A shallower sentinel can expose nested classes as apparent namespace
14037    // siblings even after a deeper sentinel proves that a containing class
14038    // owns their ranges. Drop those shadow descriptors; scope recovery starts
14039    // from the proven containing class and appends parser-visible class
14040    // ancestors, preserving the full `Outer::Inner` chain.
14041    let shadowed = recovered_classes
14042        .iter()
14043        .map(|candidate| {
14044            recovered_classes.iter().any(|container| {
14045                container.class_range.start_byte <= candidate.class_range.start_byte
14046                    && container.class_range.end_byte >= candidate.class_range.end_byte
14047                    && container.class_range != candidate.class_range
14048                    && container.namespace_scope_components.len()
14049                        > candidate.namespace_scope_components.len()
14050                    && container
14051                        .namespace_scope_components
14052                        .starts_with(&candidate.namespace_scope_components)
14053            })
14054        })
14055        .collect::<Vec<_>>();
14056    let mut index = 0usize;
14057    recovered_classes.retain(|_| {
14058        let keep = !shadowed[index];
14059        index += 1;
14060        keep
14061    });
14062    recovered_classes
14063}
14064
14065/// A flat sentinel can swallow the first class while leaving later classes and
14066/// their out-of-line definitions as ordinary declaration-list siblings.  Once
14067/// the malformed class proves the sentinel envelope, retain those structurally
14068/// complete sibling classes under the same surviving namespace so every member
14069/// owner in the region uses one recovery contract.
14070fn push_cpp_sentinel_sibling_classes<'tree>(
14071    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14072    declaration_list: Node<'tree>,
14073    sentinel_node: Node<'tree>,
14074    namespace_components: &[String],
14075    source: &str,
14076    ancestry: &ParentIndex<'tree>,
14077) {
14078    let owner_ranges =
14079        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14080    let namespace_range = cpp_declaration_range(declaration_list);
14081    let mut cursor = declaration_list.walk();
14082    for (class_node, template_node) in declaration_list
14083        .named_children(&mut cursor)
14084        .filter(|child| !same_node(*child, sentinel_node))
14085        .filter_map(cpp_sentinel_body_class_candidate)
14086    {
14087        let Some(name) = class_like_name(class_node, source, ancestry) else {
14088            continue;
14089        };
14090        if name.is_empty()
14091            || cpp_export_macro_token(&name)
14092            || cpp_complete_class_body_close(class_node).is_none()
14093        {
14094            continue;
14095        }
14096        push_cpp_sentinel_recovered_class(
14097            recovered_classes,
14098            namespace_range,
14099            namespace_components,
14100            cpp_declaration_range(template_node.unwrap_or(class_node)),
14101            name,
14102            &owner_ranges,
14103        );
14104    }
14105}
14106
14107fn push_cpp_sentinel_recovered_class(
14108    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14109    namespace_range: Range,
14110    namespace_components: &[String],
14111    class_range: Range,
14112    name: String,
14113    owner_ranges: &[CppSentinelRecoveredOwner],
14114) {
14115    let mut scope_components = namespace_components.to_vec();
14116    scope_components.push(name);
14117    let owner_ranges = owner_ranges
14118        .iter()
14119        .filter(|owner| owner.scope_components.starts_with(&scope_components))
14120        .cloned()
14121        .collect::<Vec<_>>();
14122    if recovered_classes.iter().any(|existing| {
14123        existing.class_range == class_range && existing.scope_components == scope_components
14124    }) {
14125        return;
14126    }
14127    recovered_classes.push(CppSentinelRecoveredClass {
14128        namespace_range,
14129        namespace_scope_components: namespace_components.to_vec(),
14130        class_range,
14131        scope_components,
14132        owner_ranges,
14133    });
14134}
14135
14136fn cpp_sentinel_recovered_namespace_components(
14137    function: Node<'_>,
14138    recovered_components: &[String],
14139    source: &str,
14140) -> Vec<String> {
14141    let mut ancestor_components = Vec::new();
14142    let mut ancestor = function.parent();
14143    while let Some(current) = ancestor {
14144        if current.kind() == "namespace_definition"
14145            && let Some(name_node) = current.child_by_field_name("name")
14146            && let Some(components) = cpp_name_components(name_node, source)
14147        {
14148            ancestor_components.push(
14149                components
14150                    .into_iter()
14151                    .map(|component| component.name)
14152                    .collect::<Vec<_>>(),
14153            );
14154        }
14155        ancestor = current.parent();
14156    }
14157    ancestor_components.reverse();
14158    let mut ancestors = ancestor_components
14159        .into_iter()
14160        .flatten()
14161        .collect::<Vec<_>>();
14162
14163    let overlap = (0..=ancestors.len().min(recovered_components.len()))
14164        .rev()
14165        .find(|length| {
14166            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14167        })
14168        .unwrap_or(0);
14169    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14170    ancestors
14171}
14172
14173fn cpp_sentinel_recovered_owner_ranges(
14174    body: Node<'_>,
14175    namespace_components: &[String],
14176    source: &str,
14177) -> Vec<CppSentinelRecoveredOwner> {
14178    let mut owners = Vec::new();
14179    walk_named_tree_preorder(body, true, |node| {
14180        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14181    });
14182    owners
14183}
14184
14185fn cpp_sentinel_collect_owner_range(
14186    node: Node<'_>,
14187    namespace_components: &[String],
14188    source: &str,
14189    owners: &mut Vec<CppSentinelRecoveredOwner>,
14190) -> WalkControl {
14191    if node.kind() != "function_definition" {
14192        return WalkControl::Continue;
14193    }
14194    let Some(function_declarator) = extract_function_declarator(node) else {
14195        return WalkControl::Continue;
14196    };
14197    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14198        return WalkControl::Continue;
14199    };
14200    let Some(mut components) = cpp_name_components(name_node, source) else {
14201        return WalkControl::Continue;
14202    };
14203    if components.len() <= 1 {
14204        return WalkControl::Continue;
14205    }
14206    components.pop();
14207    let mut owner_components = components
14208        .into_iter()
14209        .map(|component| component.name)
14210        .collect::<Vec<_>>();
14211    let overlap = (0..=namespace_components.len().min(owner_components.len()))
14212        .rev()
14213        .find(|length| {
14214            owner_components[..*length]
14215                == namespace_components[namespace_components.len().saturating_sub(*length)..]
14216        })
14217        .unwrap_or(0);
14218    let mut scope_components = namespace_components.to_vec();
14219    scope_components.extend(owner_components.drain(overlap..));
14220    if scope_components.len() <= namespace_components.len() {
14221        return WalkControl::Continue;
14222    }
14223    let range = cpp_declaration_range(node);
14224    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14225        existing.range == range && existing.scope_components == scope_components
14226    }) {
14227        owners.push(CppSentinelRecoveredOwner {
14228            range,
14229            owner_name_start_byte: name_node.start_byte(),
14230            namespace_component_count: namespace_components.len(),
14231            scope_components,
14232        });
14233    }
14234    WalkControl::Continue
14235}
14236
14237fn cpp_sentinel_extend_unique_owner_ranges(
14238    owners: &mut Vec<CppSentinelRecoveredOwner>,
14239    additional: Vec<CppSentinelRecoveredOwner>,
14240) {
14241    for owner in additional {
14242        if !owners.iter().any(|existing| {
14243            existing.range == owner.range && existing.scope_components == owner.scope_components
14244        }) {
14245            owners.push(owner);
14246        }
14247    }
14248}
14249
14250fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14251    if node.kind() != "ERROR" || node.named_child_count() != 1 {
14252        return false;
14253    }
14254    let Some(end_name) = node.named_child(0) else {
14255        return false;
14256    };
14257    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14258        return false;
14259    }
14260    let mut cursor = node.walk();
14261    node.children(&mut cursor)
14262        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14263}
14264
14265/// Collect owner definitions that the malformed sentinel left as later
14266/// declaration-list siblings. Parser-visible namespace siblings are a hard
14267/// boundary: their declarations must keep their own lexical namespace.
14268fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14269    parent: Node<'_>,
14270    sentinel_node: Node<'_>,
14271    namespace_components: &[String],
14272    source: &str,
14273) -> Vec<CppSentinelRecoveredOwner> {
14274    let mut owners = Vec::new();
14275    let mut after_sentinel = false;
14276    let mut cursor = parent.walk();
14277    for child in parent.named_children(&mut cursor) {
14278        if !after_sentinel {
14279            if same_node(child, sentinel_node) {
14280                after_sentinel = true;
14281            }
14282            continue;
14283        }
14284        walk_named_tree_preorder(child, true, |node| {
14285            if node.kind() == "namespace_definition" {
14286                return WalkControl::SkipChildren;
14287            }
14288            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14289        });
14290    }
14291    owners
14292}
14293
14294/// Collect owner definitions after a malformed namespace, stopping only at
14295/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
14296/// enclosing container is not trusted to belong to the recovered namespace.
14297fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14298    parent: Node<'_>,
14299    sentinel_node: Node<'_>,
14300    namespace_components: &[String],
14301    source: &str,
14302) -> Option<Vec<CppSentinelRecoveredOwner>> {
14303    let mut owners = Vec::new();
14304    let mut after_namespace = false;
14305    let mut cursor = parent.walk();
14306    for child in parent.named_children(&mut cursor) {
14307        if !after_namespace {
14308            if same_node(child, sentinel_node) {
14309                after_namespace = true;
14310            }
14311            continue;
14312        }
14313        if cpp_sentinel_namespace_end(child, source) {
14314            return Some(owners);
14315        }
14316        walk_named_tree_preorder(child, true, |node| {
14317            if node.kind() == "namespace_definition" {
14318                return WalkControl::SkipChildren;
14319            }
14320            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14321        });
14322    }
14323    None
14324}
14325
14326fn cpp_sentinel_recovered_sibling_owner_ranges(
14327    sentinel_node: Node<'_>,
14328    namespace_components: &[String],
14329    source: &str,
14330) -> Vec<CppSentinelRecoveredOwner> {
14331    let Some(declaration_list) = sentinel_node
14332        .parent()
14333        .filter(|parent| parent.kind() == "declaration_list")
14334    else {
14335        return Vec::new();
14336    };
14337    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14338        declaration_list,
14339        sentinel_node,
14340        namespace_components,
14341        source,
14342    );
14343
14344    let Some(namespace) = declaration_list
14345        .parent()
14346        .filter(|parent| parent.kind() == "namespace_definition")
14347    else {
14348        return owners;
14349    };
14350    let Some(outer_parent) = namespace.parent() else {
14351        return owners;
14352    };
14353    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14354        outer_parent,
14355        namespace,
14356        namespace_components,
14357        source,
14358    ) {
14359        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14360    }
14361    owners
14362}
14363
14364fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14365    let mut current = function_declarator.child_by_field_name("declarator")?;
14366    loop {
14367        if let Some(name) = macro_decorated_unqualified_name(current) {
14368            current = name;
14369            continue;
14370        }
14371        if matches!(
14372            current.kind(),
14373            "qualified_identifier"
14374                | "scoped_identifier"
14375                | "scoped_type_identifier"
14376                | "identifier"
14377                | "field_identifier"
14378                | "operator_name"
14379                | "destructor_name"
14380                | "literal_operator_name"
14381        ) {
14382            return Some(current);
14383        }
14384        current = current
14385            .child_by_field_name("declarator")
14386            .or_else(|| current.child_by_field_name("name"))
14387            .or_else(|| last_named_child(current))?;
14388    }
14389}
14390
14391/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
14392/// `::` between the halves. The scope is an attribute-like macro token, not
14393/// a namespace; `Name` is the declared name.
14394///
14395/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
14396/// tree-sitter joins the attribute macro and the constructor name into one
14397/// `qualified_identifier` and marks the separator it had to invent as MISSING.
14398/// That flag is the grammar's own record that no separator is in the source,
14399/// so read it instead of looking for `::` in the node text. A genuine
14400/// `Outer::Inner` keeps a present separator and is declined here, and so is
14401/// the explicit-global `::name`, whose `::` is present and whose scope is
14402/// absent.
14403fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14404    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14405        return None;
14406    }
14407    let mut cursor = node.walk();
14408    if node
14409        .children(&mut cursor)
14410        .any(|child| child.kind() == "::" && !child.is_missing())
14411    {
14412        return None;
14413    }
14414    node.child_by_field_name("name")
14415}
14416
14417fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
14418    if let Some(name) = macro_decorated_unqualified_name(node) {
14419        return cpp_name_components(name, source);
14420    }
14421    match node.kind() {
14422        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14423            let mut components = match node.child_by_field_name("scope") {
14424                Some(scope) => cpp_name_components(scope, source)?,
14425                None => Vec::new(),
14426            };
14427            let name = node.child_by_field_name("name")?;
14428            components.push(canonical_cpp_qualified_component(name, source)?);
14429            Some(components)
14430        }
14431        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
14432    }
14433}
14434
14435fn cpp_sentinel_fragment_boundary<'tree>(
14436    function: Node<'tree>,
14437    class_node: Node<'tree>,
14438    class_body: Node<'tree>,
14439    source: &str,
14440) -> Option<(Node<'tree>, Node<'tree>)> {
14441    let declaration_list = function.parent()?;
14442    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
14443        return None;
14444    }
14445    let namespace = declaration_list.parent()?;
14446    if namespace.kind() != "namespace_definition"
14447        || namespace.child_by_field_name("body") != Some(declaration_list)
14448    {
14449        return None;
14450    }
14451    let mut cursor = declaration_list.walk();
14452    let closes = declaration_list
14453        .children(&mut cursor)
14454        .filter(|child| {
14455            !child.is_named()
14456                && child.kind() == "}"
14457                && child.start_byte() >= function.end_byte()
14458                && child.start_byte() > class_node.end_byte()
14459                && child.start_byte() > class_body.start_byte()
14460        })
14461        .collect::<Vec<_>>();
14462    let [close] = closes.as_slice() else {
14463        return None;
14464    };
14465    let semicolon = namespace.next_named_sibling()?;
14466    if !cpp_is_stray_semicolon(semicolon, source)
14467        || close.end_byte() != namespace.end_byte()
14468        || semicolon.start_byte() < namespace.end_byte()
14469    {
14470        return None;
14471    }
14472    Some((*close, semicolon))
14473}
14474
14475/// Detect the bogus declaration/function tree that tree-sitter recovers for a
14476/// region prefixed by an object-like macro sentinel the parser cannot see
14477/// (issue #941), and return the byte range `[start, end)` of the swallowed
14478/// declaration interior to reparse.
14479///
14480/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
14481/// `function_definition` whose first non-comment named child is the sentinel
14482/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
14483/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
14484/// the real items.
14485/// `start` is the end of the sentinel identifier -- everything after it is the
14486/// genuine source. `end` is the node's end, extended across any trailing empty
14487/// `;` statement the mis-parse displaced past the node (the class/struct closing
14488/// semicolon), so the reparse sees a complete, brace-balanced item.
14489///
14490/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
14491/// node (`has_error`). Unknown annotation/export macros can make a real callable
14492/// error-recovered even though tree-sitter still preserves its declarator, so a
14493/// preserved callable is admitted only when a displaced class keyword precedes
14494/// that declarator. The clean-reparse-to-items gate in
14495/// `cpp_reparsed_items_are_indexable` is the final arbiter.
14496/// Return the reparse start and, when present, the structurally recovered class
14497/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
14498/// separately from the reparse start because an opaque template-declaration
14499/// macro may precede it.
14500fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
14501    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
14502    {
14503        return None;
14504    }
14505    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
14506    // retain a valid function declarator despite the unknown export macro making
14507    // the outer node erroneous. Remember that declarator for the ordering gate
14508    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
14509    // class keyword precedes a spurious callable assembled from a later member.
14510    let mut declarator_cursor = node.walk();
14511    let preserved_callable = node
14512        .children_by_field_name("declarator", &mut declarator_cursor)
14513        .find_map(extract_function_declarator);
14514    // Leading documentation comments are attached to the malformed
14515    // `function_definition` as named children.  They are not part of the
14516    // sentinel prefix, so select the first non-comment child structurally
14517    // rather than requiring the sentinel to be child zero.  This is the shape
14518    // emitted for nlohmann/json's `basic_json`: its class documentation comment
14519    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
14520    // envelope otherwise ends at the first nested union.
14521    let mut cursor = node.walk();
14522    let first = node
14523        .named_children(&mut cursor)
14524        .find(|child| child.kind() != "comment")?;
14525    if first.kind() != "type_identifier" {
14526        return None;
14527    }
14528    let sentinel = normalize_cpp_whitespace(node_text(first, source));
14529    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14530        return None;
14531    }
14532    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
14533    // makes the trailing sentinel of one region and the leading sentinel of the
14534    // next both land as bare macro-token identifiers ahead of the real content.
14535    // Advance past every leading macro-token identifier so the reparse begins at
14536    // genuine source rather than another sentinel that would re-form the bogus
14537    // shape and fail the reparse gate.
14538    let mut start = first.end_byte();
14539    let mut after_first = false;
14540    let mut cursor = node.walk();
14541    for child in node.named_children(&mut cursor) {
14542        if !after_first {
14543            if same_node(child, first) {
14544                after_first = true;
14545            }
14546            continue;
14547        }
14548        if matches!(child.kind(), "identifier" | "type_identifier")
14549            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
14550        {
14551            start = child.end_byte();
14552        } else {
14553            break;
14554        }
14555    }
14556    // An additional opaque template-declaration macro before a class can be
14557    // folded into the bogus function's qualified declarator.  In that shape
14558    // the macro is not a direct sibling we can skip above; tree-sitter exposes
14559    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
14560    // Reparse from that keyword (or a real preceding `template` keyword) so the
14561    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
14562    // a class nested in a genuine sentinel-wrapped namespace lies after the
14563    // body opening and must not change the established region start.
14564    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
14565    let mut class_start = None;
14566    let mut template_start = None;
14567    let mut stack = vec![node];
14568    while let Some(current) = stack.pop() {
14569        if current.start_byte() >= prefix_end {
14570            continue;
14571        }
14572        if matches!(
14573            current.kind(),
14574            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
14575        ) {
14576            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
14577                "class" | "struct" | "union" | "enum" => {
14578                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
14579                        seen.min(current.start_byte())
14580                    }));
14581                }
14582                "template" => {
14583                    template_start =
14584                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
14585                            seen.min(current.start_byte())
14586                        }));
14587                }
14588                _ => {}
14589            }
14590        }
14591        let mut cursor = current.walk();
14592        stack.extend(current.children(&mut cursor));
14593    }
14594    if preserved_callable.is_some_and(|callable| {
14595        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
14596    }) {
14597        return None;
14598    }
14599    if let Some(class_start) = class_start {
14600        start = template_start
14601            .filter(|template_start| *template_start < class_start)
14602            .unwrap_or(class_start);
14603    }
14604    Some((start, class_start))
14605}
14606
14607/// Locate a sentinel-prefixed class whose malformed declaration was split across
14608/// root-level siblings. The true class close is represented structurally as a
14609/// lone `}` error followed by the class's displaced `;`; nested method/body
14610/// errors are not direct siblings of the sentinel node and therefore cannot
14611/// satisfy this pair.
14612fn cpp_sentinel_macro_class_region<'tree>(
14613    node: Node<'tree>,
14614    source: &str,
14615) -> Option<(usize, usize, usize, usize, usize, usize)> {
14616    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
14617        return None;
14618    };
14619    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
14620        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
14621        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
14622    if class_start >= body_open_start {
14623        return None;
14624    }
14625    let sibling_close = {
14626        let mut sibling = node.next_named_sibling();
14627        let mut found = None;
14628        while let Some(current) = sibling {
14629            let next = current.next_named_sibling();
14630            if cpp_is_stray_close_brace(current, source)
14631                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14632            {
14633                let semicolon = next.expect("checked above");
14634                found = Some((
14635                    current.start_byte(),
14636                    semicolon.end_byte(),
14637                    semicolon.end_position().row + 1,
14638                ));
14639                break;
14640            }
14641            sibling = next;
14642        }
14643        found
14644    };
14645    // A stray `};` sibling is this class's close only when the bounded reparse
14646    // agrees the first body-bearing class ENDS there. When the malformed
14647    // envelope swallowed the class's true close, the scan can promote a much
14648    // later scope's close instead -- in protobuf-generated headers
14649    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
14650    // `struct TableStruct_*` paired with the first message class's `};`, making
14651    // the recovered "class body" span whole `namespace {}` blocks and minting
14652    // namespace-scope classes as nested members of the recovered class, which
14653    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
14654    // (#2275). On disagreement, fall through to the suffix-reparse boundary
14655    // below, which derives the close from the class node's own balanced body
14656    // range.
14657    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
14658        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14659            return false;
14660        };
14661        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14662        // A region reparse is its own tree and needs its own parent index.
14663        let reparsed_ancestry = ParentIndex::new(tree.root_node());
14664        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
14665            tree.root_node(),
14666            template_node,
14667            source,
14668            &reparsed_ancestry,
14669        ) else {
14670            return false;
14671        };
14672        let body = reparsed_class.body;
14673        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
14674    });
14675    let (class_close_start, class_close_end, class_close_line) =
14676        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
14677            (class_close_start, class_close_end, class_close_line)
14678        } else {
14679            // When the malformed envelope itself is an ERROR, tree-sitter can
14680            // leave the class's balanced close in the source while promoting
14681            // all following members to siblings. Reparse the complete suffix
14682            // and use the first body-bearing class node's own field range as
14683            // the partition boundary. This keeps balancing in tree-sitter and
14684            // preserves the source's original byte offsets.
14685            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
14686            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
14687            // A region reparse is its own tree and needs its own parent index.
14688            let reparsed_ancestry = ParentIndex::new(tree.root_node());
14689            let reparsed_class = cpp_sentinel_reparsed_class(
14690                tree.root_node(),
14691                template_node,
14692                source,
14693                &reparsed_ancestry,
14694            )?;
14695            let body = reparsed_class.body;
14696            let class_close_end = body.end_byte();
14697            let class_close_start = class_close_end.checked_sub(1)?;
14698            let class_close_line = body.end_position().row + 1;
14699            (class_close_start, class_close_end, class_close_line)
14700        };
14701    if class_close_start <= class_start {
14702        return None;
14703    }
14704
14705    // Reparse only far enough to expose the class body opening. This is a
14706    // structured check that the candidate really begins with a body-bearing
14707    // class-like item; the original malformed tree cannot provide that node.
14708    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14709    let class_root = tree.root_node();
14710    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
14711    // A region reparse is its own tree and needs its own parent index.
14712    let reparsed_ancestry = ParentIndex::new(class_root);
14713    let reparsed_class =
14714        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
14715    let body = reparsed_class.body;
14716    // The class body opening must agree with the malformed wrapper's structured
14717    // body field. This rejects an inner nested class while permitting later
14718    // members to remain fragmented as root-level siblings in the bounded parse.
14719    if body.start_byte() != body_open_start {
14720        return None;
14721    }
14722    let body_start = body.start_byte().checked_add(1)?;
14723    (body_start < class_close_start).then_some((
14724        reparse_start,
14725        class_start,
14726        body_start,
14727        class_close_start,
14728        class_close_end,
14729        class_close_line,
14730    ))
14731}
14732
14733/// Find the `{` token immediately following the class/struct/union/enum token
14734/// at `class_start` in the malformed tree. The token is anonymous in the C++
14735/// grammar, so this deliberately walks all children (not only named children)
14736/// and relies on sibling structure rather than source-text searching.
14737fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
14738    let mut stack = vec![node];
14739    while let Some(current) = stack.pop() {
14740        if current.start_byte() == class_start
14741            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
14742        {
14743            let mut sibling = current.next_sibling();
14744            while let Some(candidate) = sibling {
14745                if candidate.kind() == "{" {
14746                    return Some(candidate.start_byte());
14747                }
14748                sibling = candidate.next_sibling();
14749            }
14750        }
14751        let mut cursor = current.walk();
14752        stack.extend(current.children(&mut cursor));
14753    }
14754    None
14755}
14756
14757/// The class body that tree-sitter displaced out of a sentinel-prefixed
14758/// declaration and left as the malformed node's next sibling.
14759///
14760/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
14761/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
14762/// the last child of that `ERROR` and its `{` opens a sibling
14763/// `compound_statement` instead. The body is still the malformed tree's own
14764/// structured token, which is what the caller's `body.start_byte() !=
14765/// body_open_start` agreement check needs; it just is not reachable by walking
14766/// forward from the class token inside the node.
14767fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
14768    node.next_named_sibling()
14769        .filter(|sibling| sibling.kind() == "compound_statement")
14770}
14771
14772fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
14773    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
14774    let mut end = if class_start.is_some() {
14775        cpp_macro_prefixed_class_end(source, start)?
14776    } else {
14777        node.end_byte()
14778    };
14779    if class_start.is_none()
14780        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
14781    {
14782        end = end.max(namespace_end);
14783    }
14784    let mut sibling = node.next_named_sibling();
14785    while let Some(current) = sibling {
14786        if !cpp_is_stray_semicolon(current, source) {
14787            break;
14788        }
14789        end = current.end_byte();
14790        sibling = current.next_named_sibling();
14791    }
14792    (start < end).then_some((start, end))
14793}
14794
14795/// Extend a sentinel reparse through a following namespace that tree-sitter
14796/// flattened into the sentinel node's sibling list.
14797///
14798/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
14799/// unknown macro becomes a false function return type and consumes the first
14800/// namespace body. A second `namespace detail` then loses its enclosing node:
14801/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
14802/// attaches its declarations to the surrounding error tree. Reparse from that
14803/// structured keyword so tree-sitter, rather than a source-text brace scan,
14804/// supplies the complete namespace boundary.
14805fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
14806    let mut sibling = node.next_sibling();
14807    let keyword = loop {
14808        let candidate = sibling?;
14809        sibling = candidate.next_sibling();
14810        if candidate.kind() != "comment" {
14811            break candidate;
14812        }
14813    };
14814    if keyword.kind() != "namespace" {
14815        return None;
14816    }
14817    let name = loop {
14818        let candidate = sibling?;
14819        sibling = candidate.next_sibling();
14820        if candidate.kind() != "comment" {
14821            break candidate;
14822        }
14823    };
14824    if cpp_namespace_name_components(name, source).is_empty() {
14825        return None;
14826    }
14827    let open = loop {
14828        let candidate = sibling?;
14829        sibling = candidate.next_sibling();
14830        if candidate.kind() != "comment" {
14831            break candidate;
14832        }
14833    };
14834    if open.kind() != "{" {
14835        return None;
14836    }
14837
14838    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
14839    let root = tree.root_node();
14840    let mut cursor = root.walk();
14841    let namespace = root
14842        .named_children(&mut cursor)
14843        .find(|candidate| candidate.kind() != "comment")?;
14844    (namespace.kind() == "namespace_definition"
14845        && namespace.start_byte() == keyword.start_byte()
14846        && namespace.child_by_field_name("body").is_some())
14847    .then_some(namespace.end_byte())
14848}
14849
14850/// Parse the source suffix beginning at a structurally recovered class/template
14851/// keyword and return the end of its first body-bearing class item.  The parser,
14852/// rather than a brace scanner, owns nested-body balancing.  This is needed when
14853/// the original error tree truncates the class and scatters later members as
14854/// top-level siblings.
14855fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
14856    let tree = cpp_reparse_region_items(source, start, source.len())?;
14857    let root = tree.root_node();
14858    let mut cursor = root.walk();
14859    for item in root.named_children(&mut cursor) {
14860        if item.end_byte() <= start || item.kind() == "comment" {
14861            continue;
14862        }
14863        let mut stack = vec![item];
14864        while let Some(current) = stack.pop() {
14865            if matches!(
14866                current.kind(),
14867                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
14868            ) && cpp_body_node(current).is_some()
14869            {
14870                return Some(current.end_byte());
14871            }
14872            let mut cursor = current.walk();
14873            stack.extend(current.named_children(&mut cursor));
14874        }
14875        // The recovered prefix is required to begin with the class item.  If
14876        // the first real item is something else, fail closed rather than skip
14877        // arbitrary source looking for a later class.
14878        return None;
14879    }
14880    None
14881}
14882
14883/// An empty `;` statement: the displaced closing semicolon of a struct/class that
14884/// the sentinel mis-parse split off past the bogus function node.
14885fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
14886    node.kind() == "expression_statement"
14887        && node.named_child_count() == 0
14888        && node_text(node, source).trim() == ";"
14889}
14890
14891/// Recover the member that follows CPython's object header macro when
14892/// tree-sitter folds both declarations into one malformed field.
14893///
14894/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
14895/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
14896/// sole child is the real field declarator. The tail may wrap that identifier
14897/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
14898/// malformed wrappers do not provide the same evidence that this is the
14899/// CPython macro boundary.
14900#[derive(Clone, Copy)]
14901pub(crate) struct RecoveredPyObjectHeadField<'tree> {
14902    pub(crate) type_node: Node<'tree>,
14903    pub(crate) name: Node<'tree>,
14904    pub(crate) declarator: Node<'tree>,
14905}
14906
14907impl RecoveredPyObjectHeadField<'_> {
14908    pub(crate) fn pointer_depth(self) -> i32 {
14909        let mut depth = 0;
14910        let mut current = self.declarator;
14911        while current != self.name {
14912            debug_assert_eq!(current.kind(), "pointer_declarator");
14913            depth += 1;
14914            current = current
14915                .child_by_field_name("declarator")
14916                .expect("recovered PyObject field pointer has an inner declarator");
14917        }
14918        depth
14919    }
14920}
14921
14922pub(crate) fn recovered_pyobject_head_field<'tree>(
14923    node: Node<'tree>,
14924    source: &str,
14925) -> Option<RecoveredPyObjectHeadField<'tree>> {
14926    if node.kind() != "field_declaration" {
14927        return None;
14928    }
14929    let type_node = node.child_by_field_name("type")?;
14930    if type_node.kind() != "type_identifier"
14931        || node_text(type_node, source).trim() != "PyObject_HEAD"
14932    {
14933        return None;
14934    }
14935    let pseudo_declarator = node.child_by_field_name("declarator")?;
14936    let mut cursor = node.walk();
14937    let errors = node
14938        .named_children(&mut cursor)
14939        .filter(|child| child.kind() == "ERROR")
14940        .collect::<Vec<_>>();
14941    let [error] = errors.as_slice() else {
14942        return None;
14943    };
14944    if error.named_child_count() != 1 {
14945        return None;
14946    }
14947    let error_child = error.named_child(0)?;
14948    if pseudo_declarator.kind() == "field_identifier"
14949        && error.start_byte() >= pseudo_declarator.end_byte()
14950        && error_child.kind() == "identifier"
14951    {
14952        return Some(RecoveredPyObjectHeadField {
14953            type_node: pseudo_declarator,
14954            name: error_child,
14955            declarator: error_child,
14956        });
14957    }
14958    if pseudo_declarator.kind() != "pointer_declarator"
14959        || error.end_byte() > pseudo_declarator.start_byte()
14960        || error_child.kind() != "identifier"
14961    {
14962        return None;
14963    }
14964    let mut name = pseudo_declarator;
14965    while name.kind() == "pointer_declarator" {
14966        name = name.child_by_field_name("declarator")?;
14967    }
14968    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
14969        type_node: error_child,
14970        name,
14971        declarator: pseudo_declarator,
14972    })
14973}
14974
14975/// Recover the real field name when a leading object-like annotation macro
14976/// displaces a qualified type into tree-sitter's bit-field recovery shape.
14977///
14978/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
14979/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
14980/// `bitfield_clause` containing an error plus an assignment.  The assignment's
14981/// left field is the only structured declaration name in that malformed tail.
14982/// A real bit-field is excluded by the all-caps macro type and required error.
14983fn recovered_macro_qualified_field_declarators<'tree>(
14984    node: Node<'tree>,
14985    source: &str,
14986) -> Option<Vec<Node<'tree>>> {
14987    if node.kind() != "field_declaration" {
14988        return None;
14989    }
14990    let macro_type = node.child_by_field_name("type")?;
14991    if macro_type.kind() != "type_identifier"
14992        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
14993    {
14994        return None;
14995    }
14996    let pseudo_declarator = node.child_by_field_name("declarator")?;
14997    if pseudo_declarator.kind() != "field_identifier" {
14998        return None;
14999    }
15000    let mut cursor = node.walk();
15001    let clause = node
15002        .named_children(&mut cursor)
15003        .find(|child| child.kind() == "bitfield_clause")?;
15004    if !(0..clause.named_child_count()).any(|index| {
15005        clause
15006            .named_child(index)
15007            .is_some_and(|child| child.kind() == "ERROR")
15008    }) {
15009        return None;
15010    }
15011    let mut recovered = Vec::new();
15012    let mut stack = vec![clause];
15013    while let Some(current) = stack.pop() {
15014        if current.kind() == "assignment_expression"
15015            && let Some(left) = current.child_by_field_name("left")
15016            && extract_variable_name(left, source).is_some()
15017        {
15018            recovered.push(left);
15019            break;
15020        }
15021        let mut cursor = current.walk();
15022        stack.extend(current.named_children(&mut cursor));
15023    }
15024    if recovered.is_empty() {
15025        return None;
15026    }
15027    let mut cursor = node.walk();
15028    recovered.extend(
15029        node.children_by_field_name("declarator", &mut cursor)
15030            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15031    );
15032    Some(recovered)
15033}
15034
15035/// Recover a macro-qualified constructor that tree-sitter represents as one
15036/// field declaration. The constructor call remains inside the direct recovery
15037/// error, while each member initializer becomes a false function declarator.
15038/// The class owner proves the constructor name and lets the caller ignore those
15039/// initializer declarators.
15040fn recovered_macro_qualified_constructor_call<'tree>(
15041    node: Node<'tree>,
15042    class_name: &str,
15043    source: &str,
15044) -> Option<Node<'tree>> {
15045    if node.kind() != "field_declaration" {
15046        return None;
15047    }
15048    let macro_type = node.child_by_field_name("type")?;
15049    if macro_type.kind() != "type_identifier"
15050        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15051    {
15052        return None;
15053    }
15054    let mut cursor = node.walk();
15055    let bitfield = node
15056        .named_children(&mut cursor)
15057        .find(|child| child.kind() == "bitfield_clause")?;
15058    let error = bitfield
15059        .named_child(0)
15060        .filter(|child| child.kind() == "ERROR")?;
15061    let mut stack = vec![error];
15062    while let Some(current) = stack.pop() {
15063        if current.kind() == "call_expression"
15064            && current
15065                .child_by_field_name("function")
15066                .is_some_and(|function| node_text(function, source) == class_name)
15067            && current
15068                .child_by_field_name("arguments")
15069                .is_some_and(|arguments| arguments.kind() == "argument_list")
15070        {
15071            return Some(current);
15072        }
15073        let mut cursor = current.walk();
15074        stack.extend(current.named_children(&mut cursor));
15075    }
15076    None
15077}
15078
15079/// Recover a macro-qualified member function declaration that tree-sitter
15080/// represents as a pseudo-field. An object-like export macro before a qualified
15081/// return type can displace the namespace and type into an ERROR/bitfield
15082/// recovery, leaving the callable as a structured `call_expression`.
15083///
15084/// The caller must route this shape before ordinary declarator classification;
15085/// otherwise the displaced namespace identifier is published as a field.
15086fn recovered_macro_qualified_function_call<'tree>(
15087    node: Node<'tree>,
15088    source: &str,
15089) -> Option<Node<'tree>> {
15090    if node.kind() != "field_declaration" {
15091        return None;
15092    }
15093    let macro_type = node.child_by_field_name("type")?;
15094    if macro_type.kind() != "type_identifier"
15095        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15096    {
15097        return None;
15098    }
15099    let declarator = node.child_by_field_name("declarator")?;
15100    if declarator.kind() != "field_identifier" {
15101        return None;
15102    }
15103    let mut cursor = node.walk();
15104    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15105    if !named.iter().any(|child| {
15106        child.kind() == "storage_class_specifier"
15107            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15108    }) {
15109        return None;
15110    }
15111    let bitfield = named
15112        .iter()
15113        .find(|child| child.kind() == "bitfield_clause")?;
15114    let mut bitfield_cursor = bitfield.walk();
15115    let payload = bitfield
15116        .named_children(&mut bitfield_cursor)
15117        .collect::<Vec<_>>();
15118    let [displaced_error, call] = payload.as_slice() else {
15119        return None;
15120    };
15121    if displaced_error.kind() != "ERROR"
15122        || displaced_error.named_child_count() != 1
15123        || displaced_error
15124            .named_child(0)
15125            .is_none_or(|child| child.kind() != "identifier")
15126        || call.kind() != "call_expression"
15127        || call
15128            .child_by_field_name("function")
15129            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15130        || call
15131            .child_by_field_name("arguments")
15132            .is_none_or(|arguments| arguments.kind() != "argument_list")
15133    {
15134        return None;
15135    }
15136    Some(*call)
15137}
15138
15139fn recovered_macro_qualified_function_parameters(
15140    arguments: Node<'_>,
15141    source: &str,
15142) -> Option<(String, Vec<String>)> {
15143    if arguments.kind() != "argument_list" {
15144        return None;
15145    }
15146    let mut cursor = arguments.walk();
15147    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15148    if named.is_empty() {
15149        return Some(("()".to_string(), Vec::new()));
15150    }
15151    let mut types = Vec::new();
15152    let mut labels = Vec::new();
15153    let mut index = 0;
15154    while index < named.len() {
15155        let parameter_type = named[index];
15156        let parameter_name = named.get(index + 1).copied()?;
15157        if !matches!(
15158            parameter_type.kind(),
15159            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15160        ) || parameter_name.kind() != "ERROR"
15161            || parameter_name.named_child_count() != 1
15162            || parameter_name
15163                .named_child(0)
15164                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15165        {
15166            return None;
15167        }
15168        let parameter_name = parameter_name.named_child(0)?;
15169        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15170        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15171        index += 2;
15172    }
15173    Some((format!("({})", types.join(", ")), labels))
15174}
15175
15176/// Recognize the phantom field tree-sitter emits for a macro-qualified
15177/// function return type.  For example,
15178/// `static API result_type ThresholdForSmallA() { ... }` can become a
15179/// `field_declaration` (`API` as the type and `result_type` as a field name)
15180/// followed by a clean `function_definition` for `ThresholdForSmallA`.
15181///
15182/// Keep this predicate entirely tied to the CST envelope: the type must be an
15183/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
15184/// the declaration must carry a missing semicolon rather than a real one, and
15185/// the immediate named sibling must expose a function declarator.  A real
15186/// macro-decorated field with an explicit semicolon therefore remains a field.
15187pub fn recovered_macro_return_type_node<'tree>(
15188    node: Node<'tree>,
15189    source: &str,
15190) -> Option<Node<'tree>> {
15191    if node.kind() != "field_declaration" {
15192        return None;
15193    }
15194    let macro_type = node.child_by_field_name("type")?;
15195    if macro_type.kind() != "type_identifier"
15196        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15197    {
15198        return None;
15199    }
15200    let declarator = node.child_by_field_name("declarator")?;
15201    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15202        return None;
15203    }
15204    let mut has_missing_semicolon = false;
15205    let mut has_real_semicolon = false;
15206    for child in children_iter(node) {
15207        if child.kind() != ";" {
15208            continue;
15209        }
15210        if child.is_missing() {
15211            has_missing_semicolon = true;
15212        } else {
15213            has_real_semicolon = true;
15214        }
15215    }
15216    if !has_missing_semicolon || has_real_semicolon {
15217        return None;
15218    }
15219    let mut next = node.next_named_sibling();
15220    while next.is_some_and(|sibling| sibling.kind() == "comment") {
15221        next = next.and_then(|sibling| sibling.next_named_sibling());
15222    }
15223    let next = next?;
15224    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15225        return None;
15226    }
15227    let function_declarator = next.child_by_field_name("declarator")?;
15228    extract_function_declarator(function_declarator).map(|_| declarator)
15229}
15230
15231/// Whether `name` is a type parameter of a template declaration that lexically
15232/// encloses `node`. The malformed macro-return field uses the parameter name as
15233/// its pseudo-declarator; preserving that field is necessary to publish a
15234/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
15235/// ancestors instead of interpreting source text so nested templates and
15236/// parser-recovered regions retain their real lexical scopes.
15237pub(crate) fn cpp_active_template_type_parameter<'tree>(
15238    node: Node<'tree>,
15239    name: &str,
15240    source: &str,
15241    ancestry: &ParentIndex<'tree>,
15242) -> bool {
15243    let mut ancestor = ancestry.parent(node);
15244    while let Some(current) = ancestor {
15245        if current.kind() == "template_declaration"
15246            && let Some(parameters) = current.child_by_field_name("parameters")
15247        {
15248            let mut cursor = parameters.walk();
15249            if parameters.named_children(&mut cursor).any(|parameter| {
15250                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15251                    && cpp_template_parameter_name(parameter, source)
15252                        .is_some_and(|parameter_name| parameter_name == name)
15253            }) {
15254                return true;
15255            }
15256        }
15257        ancestor = ancestry.parent(current);
15258    }
15259    false
15260}
15261
15262/// Reparse the region `[start, end)` of `source` as C++, confined to the region
15263/// via included ranges so every reparsed node keeps its original byte offset and
15264/// line number. The existing visitors read node text from the original source,
15265/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
15266/// `parse_rust_region_tree` technique.
15267fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15268    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15269}
15270
15271fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15272    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15273        return None;
15274    }
15275    let semicolon = node.next_sibling()?;
15276    if semicolon.kind() != ";" || semicolon.is_missing() {
15277        return None;
15278    }
15279    let row = node.start_position().row;
15280    let mut start = node.start_byte();
15281    let mut sibling = node.prev_sibling();
15282    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15283        if previous.kind() == ";" {
15284            break;
15285        }
15286        start = previous.start_byte();
15287        sibling = previous.prev_sibling();
15288    }
15289    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15290}
15291
15292/// One `RET name _(( args ));`-style K&R prototype-macro invocation
15293/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
15294/// byte spans a clean reparse needs, in original-source order.
15295struct PrototypeMacroCandidate {
15296    /// Where the return type and declared name begin.
15297    run_start: usize,
15298    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
15299    /// exclusive end of the first reparse span.
15300    identifier_start: usize,
15301    /// The inner `(`'s start byte: the start of the second reparse span.
15302    /// This paren is kept so the reparse sees exactly one parameter-list
15303    /// paren pair.
15304    inner_open_start: usize,
15305    /// Byte just past the inner `)`: the end of the second reparse span.
15306    inner_close_end: usize,
15307    /// Byte just past the outer `)`: the start of the third reparse span.
15308    outer_close_end: usize,
15309    /// Byte just past the terminating `;`: the end of the third reparse
15310    /// span, and of the whole candidate.
15311    semicolon_end: usize,
15312}
15313
15314impl PrototypeMacroCandidate {
15315    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
15316    /// one included-range tree: the return type and name, the parenthesized
15317    /// argument list, and the terminating `;`. The macro token and its outer
15318    /// wrapping parenthesis are omitted -- deleting them is exactly what the
15319    /// (absent) preprocessor macro expansion would do.
15320    fn ranges(&self) -> [(usize, usize); 3] {
15321        [
15322            (self.run_start, self.identifier_start),
15323            (self.inner_open_start, self.inner_close_end),
15324            (self.outer_close_end, self.semicolon_end),
15325        ]
15326    }
15327}
15328
15329/// A live terminating semicolon owned directly by `node`.
15330fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15331    node.child(node.child_count().checked_sub(1)?)
15332        .filter(|child| child.kind() == ";" && !child.is_missing())
15333}
15334
15335/// The known pre-ANSI prototype macros whose expansion is exactly their one
15336/// argument. The malformed CST cannot prove that an arbitrary identifier has
15337/// that definition, so unknown spellings fail closed rather than turning an
15338/// ordinary broken declaration into a Function (issue #2932).
15339fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15340    matches!(
15341        node.kind(),
15342        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15343    ) && matches!(
15344        normalize_cpp_whitespace(node_text(node, source)).as_str(),
15345        "_" | "__P" | "OF" | "PROTO"
15346    )
15347}
15348
15349/// Read tree-sitter's recovery for `declared_name MACRO`: a
15350/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
15351/// declared name, and whose `name` is a known prototype macro.
15352fn cpp_prototype_macro_qualified_parts<'tree>(
15353    node: Node<'tree>,
15354    source: &str,
15355) -> Option<(Node<'tree>, Node<'tree>)> {
15356    if node.kind() != "qualified_identifier" {
15357        return None;
15358    }
15359    let declared_name = node
15360        .child_by_field_name("scope")
15361        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15362    let macro_name = macro_decorated_unqualified_name(node)?;
15363    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15364}
15365
15366/// The inner parenthesized node of tree-sitter's structured
15367/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
15368/// the macro invocation's outer parentheses while retaining the one pair an
15369/// ordinary function declarator needs. No delimiter search is involved: both
15370/// pairs and their ownership come from the CST.
15371fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15372    if arguments.kind() != "argument_list"
15373        || arguments.named_child_count() != 1
15374        || arguments.child_count() != 3
15375        || arguments
15376            .child(0)
15377            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15378        || arguments
15379            .child(2)
15380            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15381    {
15382        return None;
15383    }
15384    let inner = arguments.named_child(0)?;
15385    let close_index = match inner.kind() {
15386        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15387        // tree-sitter completes the C++ cast production with a zero-width
15388        // value after the live close parenthesis.
15389        "cast_expression" => inner.child_count().checked_sub(2)?,
15390        _ => return None,
15391    };
15392    (inner
15393        .child(0)
15394        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15395        && inner
15396            .child(close_index)
15397            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15398    .then_some(inner)
15399}
15400
15401fn cpp_prototype_macro_candidate_from_init_declaration(
15402    declaration: Node<'_>,
15403    source: &str,
15404) -> Option<PrototypeMacroCandidate> {
15405    let init = declaration
15406        .child_by_field_name("declarator")
15407        .filter(|declarator| declarator.kind() == "init_declarator")?;
15408    let malformed_declarator = init.child_by_field_name("declarator")?;
15409    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
15410        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
15411    } else {
15412        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
15413            return None;
15414        }
15415        let declared_name_error = init
15416            .prev_named_sibling()
15417            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
15418        let declared_name = declared_name_error
15419            .named_child(0)
15420            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
15421        (declared_name, malformed_declarator)
15422    };
15423    let arguments = init
15424        .child_by_field_name("value")
15425        .filter(|value| value.kind() == "argument_list")?;
15426    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15427    let semicolon = cpp_direct_semicolon(declaration)?;
15428    let return_type = declaration.child_by_field_name("type")?;
15429    if return_type.end_byte() > declared_name.start_byte()
15430        || declared_name.end_byte() > macro_name.start_byte()
15431        || macro_name.end_byte() > arguments.start_byte()
15432        || arguments.end_byte() > semicolon.start_byte()
15433    {
15434        return None;
15435    }
15436    Some(PrototypeMacroCandidate {
15437        run_start: declaration.start_byte(),
15438        identifier_start: macro_name.start_byte(),
15439        inner_open_start: inner.start_byte(),
15440        inner_close_end: inner.end_byte(),
15441        outer_close_end: arguments.end_byte(),
15442        semicolon_end: semicolon.end_byte(),
15443    })
15444}
15445
15446fn cpp_prototype_macro_candidate_from_qualified_declaration(
15447    declaration: Node<'_>,
15448    source: &str,
15449) -> Option<PrototypeMacroCandidate> {
15450    let qualified = declaration
15451        .child_by_field_name("declarator")
15452        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
15453    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15454    let open_error = qualified
15455        .next_named_sibling()
15456        .filter(|next| next.kind() == "ERROR")?;
15457    let close_error = last_named_child(declaration)
15458        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
15459    if open_error.child_count() < 3
15460        || open_error
15461            .child(0)
15462            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15463        || open_error
15464            .child(1)
15465            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15466        || close_error.child_count() != 2
15467        || close_error
15468            .child(0)
15469            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15470        || close_error
15471            .child(1)
15472            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15473    {
15474        return None;
15475    }
15476    let inner_open = open_error.child(1)?;
15477    let inner_close = close_error.child(0)?;
15478    let outer_close = close_error.child(1)?;
15479    let semicolon = cpp_direct_semicolon(declaration)?;
15480    let return_type = declaration.child_by_field_name("type")?;
15481    if return_type.end_byte() > qualified.start_byte()
15482        || macro_name.end_byte() > open_error.start_byte()
15483        || inner_open.start_byte() > inner_close.end_byte()
15484        || inner_close.end_byte() > outer_close.start_byte()
15485        || outer_close.end_byte() > semicolon.start_byte()
15486    {
15487        return None;
15488    }
15489    Some(PrototypeMacroCandidate {
15490        run_start: declaration.start_byte(),
15491        identifier_start: macro_name.start_byte(),
15492        inner_open_start: inner_open.start_byte(),
15493        inner_close_end: inner_close.end_byte(),
15494        outer_close_end: outer_close.end_byte(),
15495        semicolon_end: semicolon.end_byte(),
15496    })
15497}
15498
15499fn cpp_prototype_macro_candidate_from_pointer_expression(
15500    statement: Node<'_>,
15501    source: &str,
15502) -> Option<PrototypeMacroCandidate> {
15503    if statement.kind() != "expression_statement"
15504        || statement.named_child_count() != 1
15505        || !statement.has_error()
15506    {
15507        return None;
15508    }
15509    let expansion = statement
15510        .named_child(0)
15511        .filter(|child| child.kind() == "parameter_pack_expansion")?;
15512    let binary = expansion
15513        .child_by_field_name("pattern")
15514        .filter(|pattern| pattern.kind() == "binary_expression")?;
15515    if binary.child_count() != 3
15516        || binary
15517            .child(1)
15518            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
15519        || expansion
15520            .child(expansion.child_count().checked_sub(1)?)
15521            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
15522    {
15523        return None;
15524    }
15525    let return_type = binary.child_by_field_name("left")?;
15526    let call = binary
15527        .child_by_field_name("right")
15528        .filter(|right| right.kind() == "call_expression")?;
15529    let qualified = call.child_by_field_name("function")?;
15530    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
15531    let arguments = call
15532        .child_by_field_name("arguments")
15533        .filter(|arguments| arguments.kind() == "argument_list")?;
15534    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
15535    let semicolon = cpp_direct_semicolon(statement)?;
15536    if return_type.end_byte() > declared_name.start_byte()
15537        || macro_name.end_byte() > arguments.start_byte()
15538        || arguments.end_byte() > semicolon.start_byte()
15539    {
15540        return None;
15541    }
15542    Some(PrototypeMacroCandidate {
15543        run_start: statement.start_byte(),
15544        identifier_start: macro_name.start_byte(),
15545        inner_open_start: inner.start_byte(),
15546        inner_close_end: inner.end_byte(),
15547        outer_close_end: arguments.end_byte(),
15548        semicolon_end: semicolon.end_byte(),
15549    })
15550}
15551
15552/// Recover only CST shapes whose fields preserve the declaration name, macro
15553/// invocation, nested argument list, and terminator. Arbitrary flattened
15554/// `ERROR` wreckage stays unsupported rather than being interpreted through a
15555/// token or delimiter scan (issue #2932).
15556fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
15557    let candidate = match node.kind() {
15558        "declaration" if node.has_error() => {
15559            cpp_prototype_macro_candidate_from_init_declaration(node, source)
15560                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
15561        }
15562        "expression_statement" => {
15563            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
15564        }
15565        _ => None,
15566    };
15567    candidate.into_iter().collect()
15568}
15569
15570fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
15571    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
15572        return false;
15573    }
15574    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
15575        return false;
15576    }
15577    let Some(declarator) = (if node.kind() == "function_definition" {
15578        node.child_by_field_name("declarator")
15579            .and_then(extract_function_declarator)
15580    } else {
15581        node.named_child(0)
15582            .filter(|child| child.kind() == "function_declarator")
15583    }) else {
15584        return false;
15585    };
15586    let Some(name) = cpp_function_declarator_name_node(declarator) else {
15587        return false;
15588    };
15589    declarator.start_byte() == node.start_byte()
15590        && name.kind() == "identifier"
15591        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
15592}
15593
15594/// Reparse a fragmented class-body interior while preserving its original byte
15595/// and line offsets, confined to the region by tree-sitter included ranges.
15596///
15597/// This used to materialize the region's whole file prefix as whitespace and
15598/// make the lexer walk it, the technique #1309 replaced on the other reparse
15599/// path: O(file) per fragmented-class recovery, on files that are already
15600/// error-recovered and already slow (#2788). Included ranges give the parser
15601/// the same view -- the region's bytes, at their original offsets and
15602/// line/column positions -- without materializing or lexing anything before it.
15603///
15604/// The equality of the two views is the claim, so a debug build parses both and
15605/// asserts the trees agree node for node.
15606fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15607    let region = cpp_reparse_region_items(source, start, end);
15608
15609    #[cfg(debug_assertions)]
15610    assert_eq!(
15611        region.as_ref().map(cpp_tree_shape),
15612        cpp_reparse_padded_class_body(source, start, end)
15613            .as_ref()
15614            .map(cpp_tree_shape),
15615        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
15616         prefix produces"
15617    );
15618
15619    region
15620}
15621
15622/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
15623/// replaces, kept as the oracle a debug build asserts every region reparse
15624/// against and as the release-mode parity tests' reference (#2788).
15625#[cfg(any(debug_assertions, test))]
15626fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
15627    if start >= end {
15628        // The included-range parser refuses an empty region; the padded one
15629        // returned a tree holding nothing, which every caller read as "no
15630        // members here".
15631        return None;
15632    }
15633    let bytes = source.as_bytes();
15634    let prefix = bytes.get(..start)?;
15635    let interior = bytes.get(start..end)?;
15636    let mut padded = Vec::with_capacity(end);
15637    padded.extend(
15638        prefix
15639            .iter()
15640            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
15641    );
15642    padded.extend_from_slice(interior);
15643    let padded = String::from_utf8(padded).ok()?;
15644    let mut parser = Parser::new();
15645    parser
15646        .set_language(&tree_sitter_cpp::LANGUAGE.into())
15647        .ok()?;
15648    parser.parse(&padded, None)
15649}
15650
15651/// Every node of `tree` in preorder, by kind, span and position, which is what
15652/// two reparses of one region have to agree on for their callers to read the
15653/// same declarations out of either (#2788).
15654#[cfg(any(debug_assertions, test))]
15655fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
15656    let mut shape = Vec::new();
15657    let mut cursor = tree.root_node().walk();
15658    let mut stack = vec![tree.root_node()];
15659    while let Some(node) = stack.pop() {
15660        shape.push((
15661            node.kind(),
15662            node.start_byte(),
15663            node.end_byte(),
15664            node.start_position().row,
15665            node.start_position().column,
15666            node.is_named(),
15667            node.is_missing(),
15668        ));
15669        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
15670        stack.extend(children.into_iter().rev());
15671    }
15672    shape
15673}
15674
15675/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
15676/// reparsed interior is indexed only when every top-level named node is a
15677/// well-formed C++ item (or a comment) and at least one real item is present.
15678/// Expression/statement soup surfaces as a top-level `ERROR` or
15679/// `expression_statement`, neither of which is an item kind, so it is rejected.
15680///
15681/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
15682/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
15683/// swallowed by a preceding dangling sentinel) reparses to a real
15684/// `namespace_definition` whose body still holds a bogus `function_definition`,
15685/// so the subtree legitimately carries an error. Container items are admitted
15686/// even with an internal error; the inner bogus function is recovered recursively
15687/// when `visit_function_definition` walks it. Each recursion strips at least one
15688/// leading sentinel, so the region strictly shrinks and recovery terminates.
15689///
15690/// A top-level `function_definition` is the one place we stay strict: it is
15691/// admitted only when it is clean or is itself a sentinel candidate. A function
15692/// that has an error and is not a sentinel is a real callable with a broken body,
15693/// so we refuse the whole reparse and let the ordinary path handle it (preserving
15694/// its real return type rather than re-deriving an implicit one).
15695fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
15696    let mut cursor = root.walk();
15697    let mut saw_item = false;
15698    for child in root.named_children(&mut cursor) {
15699        match child.kind() {
15700            "comment" => {}
15701            "function_definition" => {
15702                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
15703                    return false;
15704                }
15705                saw_item = true;
15706            }
15707            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
15708            _ => return false,
15709        }
15710    }
15711    saw_item
15712}
15713
15714/// Robustness gate for a reparsed fragmented multiple-base export class body
15715/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
15716/// kinds a class body produces when reparsed at translation-unit scope: the
15717/// access-specifier label preceding the first member surfaces as a
15718/// `labeled_statement` wrapping that member, and members surface as
15719/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
15720/// Statement or expression soup surfaces as other top-level kinds and is rejected,
15721/// so only a genuinely member-shaped body is ever re-owned as members; anything
15722/// ambiguous falls back to indexing the class alone.
15723fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
15724    if node.kind() != "ERROR" {
15725        return false;
15726    }
15727    let mut stack = Vec::new();
15728    let mut saw_function_declarator = false;
15729    let mut cursor = node.walk();
15730    for child in node.named_children(&mut cursor) {
15731        stack.push(child);
15732    }
15733    while let Some(current) = stack.pop() {
15734        match current.kind() {
15735            // Tree-sitter may wrap adjacent copy-control declarations in a
15736            // nested ERROR. Keep descending only through ERROR wrappers; the
15737            // actual declaration payload must be a function_declarator.
15738            "ERROR" => {
15739                let mut cursor = current.walk();
15740                stack.extend(current.named_children(&mut cursor));
15741            }
15742            "function_declarator" => saw_function_declarator = true,
15743            _ => return false,
15744        }
15745    }
15746    saw_function_declarator
15747}
15748
15749fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
15750    if node.kind() != "ERROR" {
15751        return false;
15752    }
15753    let mut cursor = node.walk();
15754    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15755    let [explicit, constructor_error, destructor] = named.as_slice() else {
15756        return false;
15757    };
15758    let Some(constructor) = constructor_error.named_child(0) else {
15759        return false;
15760    };
15761    let Some(constructor_name) =
15762        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
15763    else {
15764        return false;
15765    };
15766    let Some(destructor_name) =
15767        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
15768    else {
15769        return false;
15770    };
15771    let Some(destroyed_type) = destructor_name.named_child(0) else {
15772        return false;
15773    };
15774    explicit.kind() == "explicit_function_specifier"
15775        && constructor_error.kind() == "ERROR"
15776        && constructor_error.named_child_count() == 1
15777        && constructor.kind() == "function_declarator"
15778        && constructor_name.kind() == "identifier"
15779        && destructor.kind() == "function_declarator"
15780        && destructor_name.kind() == "destructor_name"
15781        && destroyed_type.kind() == "identifier"
15782        && node_text(constructor_name, source) == node_text(destroyed_type, source)
15783}
15784
15785fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
15786    if node.kind() != "compound_statement" {
15787        return false;
15788    }
15789    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
15790        return false;
15791    };
15792    if prefix.kind() == "labeled_statement"
15793        && prefix.named_child(0).is_some_and(|label| {
15794            matches!(
15795                node_text(label, source).trim(),
15796                "public" | "private" | "protected"
15797            )
15798        })
15799    {
15800        return prefix.named_children(&mut prefix.walk()).any(|child| {
15801            child.kind() == "declaration"
15802                && child.has_error()
15803                && child
15804                    .named_children(&mut child.walk())
15805                    .any(cpp_reparsed_member_error_is_indexable)
15806        });
15807    }
15808    // A malformed constructor initializer can be split into a declaration
15809    // followed by its compound body when the class prefix already contains
15810    // realistic members. Keep this admission tied to that exact structured
15811    // declaration/error/body chain rather than accepting arbitrary blocks.
15812    prefix.kind() == "declaration"
15813        && prefix.has_error()
15814        && prefix
15815            .named_children(&mut prefix.walk())
15816            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
15817}
15818
15819fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
15820    if !cpp_reparsed_member_error_is_indexable(node) {
15821        return false;
15822    }
15823    let Some(preproc) = node.next_named_sibling() else {
15824        return false;
15825    };
15826    preproc.kind() == "preproc_if"
15827        && preproc.has_error()
15828        && preproc
15829            .named_children(&mut preproc.walk())
15830            .any(|child| child.kind() == "expression_statement" && child.has_error())
15831        && preproc
15832            .next_named_sibling()
15833            .is_some_and(|body| body.kind() == "compound_statement")
15834}
15835
15836/// Return a function body whose braces and ownership are explicit in the
15837/// reparsed class-member tree. An error below a real function envelope is
15838/// recoverable by the ordinary function visitor; a missing/deferred body is
15839/// not, because accepting it would let statement soup masquerade as a member.
15840fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
15841    if node.kind() != "function_definition" {
15842        return None;
15843    }
15844    let body = node.child_by_field_name("body")?;
15845    if body.kind() != "compound_statement" {
15846        return None;
15847    }
15848    let open = body.child(0)?;
15849    let close = body.child(body.child_count().checked_sub(1)?)?;
15850    if open.kind() != "{"
15851        || open.is_missing()
15852        || close.kind() != "}"
15853        || close.is_missing()
15854        || close.end_byte() != body.end_byte()
15855        || body.end_byte() != node.end_byte()
15856    {
15857        return None;
15858    }
15859    Some(body)
15860}
15861
15862fn cpp_reparsed_member_function_errors_are_in_body(
15863    node: Node<'_>,
15864    body: Node<'_>,
15865    source: &str,
15866) -> bool {
15867    let mut cursor = node.walk();
15868    node.children(&mut cursor).all(|child| {
15869        same_node(child, body)
15870            || cpp_reparsed_member_attribute_error(child, source)
15871            || cpp_reparsed_member_signature_identifier_errors(child)
15872            || (!child.has_error() && !child.is_error() && !child.is_missing())
15873    })
15874}
15875
15876/// A complete callable can still carry parser errors in its signature when a
15877/// project annotation is not part of the C++ grammar (`nonneg int`,
15878/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
15879/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
15880/// leaves inside the already-proven callable envelope; structured statements,
15881/// literals, missing tokens, and other malformed signature payload remain
15882/// rejected.
15883fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
15884    if !node.has_error() && !node.is_error() && !node.is_missing() {
15885        return false;
15886    }
15887    let mut stack = vec![node];
15888    let mut saw_error = false;
15889    while let Some(current) = stack.pop() {
15890        if current.is_missing() {
15891            return false;
15892        }
15893        if current.kind() == "ERROR" {
15894            saw_error = true;
15895            let mut cursor = current.walk();
15896            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
15897            if children
15898                .iter()
15899                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
15900            {
15901                return false;
15902            }
15903            stack.extend(children);
15904            continue;
15905        }
15906        let mut cursor = current.walk();
15907        stack.extend(current.children(&mut cursor));
15908    }
15909    saw_error
15910}
15911
15912fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
15913    node.kind() == "ERROR"
15914        && node.named_child_count() == 1
15915        && node.named_child(0).is_some_and(|attribute| {
15916            attribute.kind() == "identifier"
15917                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
15918        })
15919}
15920
15921/// A C++ attribute placed between a member's declarator and body can make
15922/// tree-sitter expose the callable as
15923/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
15924/// Keep this admission tied to that exact node geometry. In particular, an
15925/// arbitrary ERROR or identifier before a compound statement is not enough.
15926fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
15927    let Some(body) = cpp_reparsed_member_function_body(node) else {
15928        return false;
15929    };
15930    let mut cursor = node.walk();
15931    let named = node
15932        .named_children(&mut cursor)
15933        .filter(|child| child.kind() != "comment")
15934        .collect::<Vec<_>>();
15935    let [type_node, error, attribute, body_node] = named.as_slice() else {
15936        return false;
15937    };
15938    if !same_node(*body_node, body)
15939        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
15940        || attribute.kind() != "identifier"
15941        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
15942        || error.kind() != "ERROR"
15943        || error.named_child_count() != 1
15944    {
15945        return false;
15946    }
15947    error
15948        .named_child(0)
15949        .is_some_and(cpp_reparsed_attribute_callable_declarator)
15950}
15951
15952fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
15953    cpp_structured_type_path(node, source).is_some()
15954        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
15955}
15956
15957fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15958    let Some(body) = cpp_reparsed_member_function_body(node) else {
15959        return false;
15960    };
15961    let mut cursor = node.walk();
15962    let named = node
15963        .named_children(&mut cursor)
15964        .filter(|child| child.kind() != "comment")
15965        .collect::<Vec<_>>();
15966    let [friend, return_error, declarator, body_node] = named.as_slice() else {
15967        return false;
15968    };
15969    let Some(return_type) = return_error.named_child(0) else {
15970        return false;
15971    };
15972    same_node(*body_node, body)
15973        && friend.kind() == "type_identifier"
15974        && node_text(*friend, source) == "friend"
15975        && return_error.kind() == "ERROR"
15976        && return_error.named_child_count() == 1
15977        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
15978        && extract_function_declarator(*declarator)
15979            .and_then(cpp_function_declarator_name_node)
15980            .is_some()
15981}
15982
15983fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
15984    let Some(body) = cpp_reparsed_member_function_body(node) else {
15985        return false;
15986    };
15987    let mut cursor = node.walk();
15988    let named = node
15989        .named_children(&mut cursor)
15990        .filter(|child| child.kind() != "comment")
15991        .collect::<Vec<_>>();
15992    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
15993        return false;
15994    };
15995    let Some(return_type) = return_error.named_child(0) else {
15996        return false;
15997    };
15998    same_node(*body_node, body)
15999        && prefix
16000            .iter()
16001            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16002        && attribute.kind() == "type_identifier"
16003        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16004        && return_error.kind() == "ERROR"
16005        && return_error.named_child_count() == 1
16006        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16007        && extract_function_declarator(*declarator)
16008            .and_then(cpp_function_declarator_name_node)
16009            .is_some()
16010}
16011
16012/// An included-range reparse that begins inside a malformed class can merge an
16013/// access label and following template member. Tree-sitter then emits the label
16014/// as the `template_type` name, the template parameter list as its arguments,
16015/// an ERROR-wrapped return type, the callable declarator, and its complete
16016/// body. Admit only that exact structured displacement.
16017fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16018    let Some(body) = cpp_reparsed_member_function_body(node) else {
16019        return false;
16020    };
16021    let mut cursor = node.walk();
16022    let named = node
16023        .named_children(&mut cursor)
16024        .filter(|child| child.kind() != "comment")
16025        .collect::<Vec<_>>();
16026    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16027        return false;
16028    };
16029    let Some(template_name) = template_type.child_by_field_name("name") else {
16030        return false;
16031    };
16032    let Some(arguments) = template_type.child_by_field_name("arguments") else {
16033        return false;
16034    };
16035    let Some(return_type) = return_error.named_child(0) else {
16036        return false;
16037    };
16038    let mut cursor = template_type.walk();
16039    let template_errors = template_type
16040        .named_children(&mut cursor)
16041        .filter(|child| child.kind() == "ERROR")
16042        .collect::<Vec<_>>();
16043    let [comment_error] = template_errors.as_slice() else {
16044        return false;
16045    };
16046    let mut cursor = comment_error.walk();
16047    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16048    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16049        return false;
16050    };
16051    same_node(*body_node, body)
16052        && template_type.kind() == "template_type"
16053        && template_name.kind() == "type_identifier"
16054        && matches!(
16055            node_text(template_name, source).trim(),
16056            "public" | "private" | "protected"
16057        )
16058        && arguments.kind() == "template_argument_list"
16059        && arguments.named_child_count() > 0
16060        && !arguments.has_error()
16061        && !colon.is_named()
16062        && colon.kind() == ":"
16063        && comments.iter().all(|child| child.kind() == "comment")
16064        && !template_keyword.is_named()
16065        && template_keyword.kind() == "template"
16066        && return_error.kind() == "ERROR"
16067        && return_error.named_child_count() == 1
16068        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16069        && extract_function_declarator(*declarator)
16070            .and_then(cpp_function_declarator_name_node)
16071            .is_some()
16072}
16073
16074/// Return the constructor declaration tree-sitter can merge into an access
16075/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
16076/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
16077/// declaration's apparent type; the callable name must still exactly match the
16078/// recovered class, so unrelated labeled statements are never re-owned.
16079fn cpp_reparsed_preprocessor_constructor<'tree>(
16080    node: Node<'tree>,
16081    class_name: &str,
16082    source: &str,
16083) -> Option<Node<'tree>> {
16084    if node.kind() != "labeled_statement" {
16085        return None;
16086    }
16087    let mut cursor = node.walk();
16088    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16089    let [label, directive_error, declaration] = named.as_slice() else {
16090        return None;
16091    };
16092    if label.kind() != "statement_identifier"
16093        || !matches!(
16094            node_text(*label, source),
16095            "public" | "private" | "protected"
16096        )
16097        || directive_error.kind() != "ERROR"
16098        || directive_error.child_count() != 1
16099        || directive_error
16100            .child(0)
16101            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16102        || declaration.kind() != "declaration"
16103        || declaration.named_child_count() != 2
16104    {
16105        return None;
16106    }
16107    let apparent_type = declaration.child_by_field_name("type")?;
16108    if apparent_type.kind() != "type_identifier"
16109        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16110    {
16111        return None;
16112    }
16113    let declarator = declaration.child_by_field_name("declarator")?;
16114    let function = extract_function_declarator(declarator)?;
16115    let name = cpp_function_declarator_name_node(function)?;
16116    (node_text(name, source) == class_name).then_some(*declaration)
16117}
16118
16119fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16120    if extract_function_declarator(node)
16121        .and_then(cpp_function_declarator_name_node)
16122        .is_some()
16123    {
16124        return true;
16125    }
16126    node.kind() == "init_declarator"
16127        && node
16128            .child_by_field_name("declarator")
16129            .is_some_and(|declarator| declarator.kind() == "identifier")
16130        && node
16131            .child_by_field_name("value")
16132            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16133}
16134
16135/// Return true for the constrained/attribute form that tree-sitter splits into
16136/// an ERROR declaration, a preprocessor `requires` clause, and a following
16137/// compound statement. The three nodes must remain immediate named siblings;
16138/// this deliberately does not search source text or skip unrelated statements.
16139fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16140    if node.kind() != "ERROR" || node.named_child_count() != 3 {
16141        return false;
16142    }
16143    let mut cursor = node.walk();
16144    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16145    let [type_node, function_declarator, attribute] = named.as_slice() else {
16146        return false;
16147    };
16148    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16149        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16150        || attribute.kind() != "identifier"
16151        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16152    {
16153        return false;
16154    }
16155    let Some(preproc) =
16156        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16157    else {
16158        return false;
16159    };
16160    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16161        .filter(|sibling| sibling.kind() == "compound_statement")
16162    else {
16163        return false;
16164    };
16165    let Some(open) = body.child(0) else {
16166        return false;
16167    };
16168    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16169        return false;
16170    };
16171    let Some(condition) = preproc.child_by_field_name("condition") else {
16172        return false;
16173    };
16174    let mut cursor = preproc.walk();
16175    let payload = preproc
16176        .named_children(&mut cursor)
16177        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16178        .collect::<Vec<_>>();
16179    let [requires_statement] = payload.as_slice() else {
16180        return false;
16181    };
16182    let requires_clause = requires_statement.named_child(0);
16183
16184    open.kind() == "{"
16185        && !open.is_missing()
16186        && close.kind() == "}"
16187        && !close.is_missing()
16188        && close.end_byte() == body.end_byte()
16189        && requires_statement.kind() == "expression_statement"
16190        && requires_statement.named_child_count() == 1
16191        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16192}
16193
16194fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16195    let mut sibling = node.next_named_sibling();
16196    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16197        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16198    }
16199    sibling
16200}
16201
16202fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16203    let mut sibling = node.prev_named_sibling();
16204    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16205        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16206    }
16207    sibling
16208}
16209
16210fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16211    let Some(preproc) =
16212        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16213    else {
16214        return false;
16215    };
16216    let Some(error) =
16217        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16218    else {
16219        return false;
16220    };
16221    cpp_reparsed_attribute_requires_error(error, source)
16222}
16223
16224fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16225    node: Node<'tree>,
16226    source: &str,
16227) -> Option<Node<'tree>> {
16228    if node.kind() != "ERROR" {
16229        return None;
16230    }
16231    let mut cursor = node.walk();
16232    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16233    let [parameter, macro_name, message] = named.as_slice() else {
16234        return None;
16235    };
16236    let parameter_name = parameter.named_child(0)?;
16237    (parameter.kind() == "type_parameter_declaration"
16238        && parameter_name.kind() == "type_identifier"
16239        && macro_name.kind() == "type_identifier"
16240        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16241        && message.kind() == "string_literal")
16242        .then_some(parameter_name)
16243}
16244
16245/// Recognize the alternate constraint-macro prefix where tree-sitter retains
16246/// the complete qualified constraint as a fourth child instead of moving it
16247/// into the following function. Keep the gate tied to a two-type template
16248/// constraint that names the declared type parameter.
16249fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16250    node: Node<'tree>,
16251    source: &str,
16252) -> Option<Node<'tree>> {
16253    if node.kind() != "ERROR" {
16254        return None;
16255    }
16256    let mut cursor = node.walk();
16257    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16258    let [parameter, macro_name, message, constraint] = named.as_slice() else {
16259        return None;
16260    };
16261    let parameter_name = parameter.named_child(0)?;
16262    let constraint_scope = constraint.child_by_field_name("scope")?;
16263    let constraint_template = constraint.child_by_field_name("name")?;
16264    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16265    let mut argument_cursor = constraint_arguments.walk();
16266    let constraint_types = constraint_arguments
16267        .named_children(&mut argument_cursor)
16268        .collect::<Vec<_>>();
16269    if parameter.kind() != "type_parameter_declaration"
16270        || parameter_name.kind() != "type_identifier"
16271        || macro_name.kind() != "type_identifier"
16272        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16273        || message.kind() != "string_literal"
16274        || constraint.kind() != "qualified_identifier"
16275        || constraint_scope.kind() != "namespace_identifier"
16276        || !matches!(
16277            constraint_template.kind(),
16278            "template_function" | "template_type"
16279        )
16280        || !matches!(constraint_types.as_slice(), [left, right]
16281            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16282        || constraint_arguments.has_error()
16283    {
16284        return None;
16285    }
16286    let parameter_text = node_text(parameter_name, source);
16287    let mut stack = constraint_types;
16288    while let Some(current) = stack.pop() {
16289        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16290            return Some(parameter_name);
16291        }
16292        let mut cursor = current.walk();
16293        stack.extend(current.named_children(&mut cursor));
16294    }
16295    None
16296}
16297
16298fn cpp_reparsed_template_macro_companion_is_indexable(
16299    node: Node<'_>,
16300    parameter_name: Node<'_>,
16301    source: &str,
16302) -> bool {
16303    let Some(body) = cpp_reparsed_member_function_body(node) else {
16304        return false;
16305    };
16306    let mut cursor = node.walk();
16307    let named = node
16308        .named_children(&mut cursor)
16309        .filter(|child| child.kind() != "comment")
16310        .collect::<Vec<_>>();
16311    let [
16312        constraint,
16313        close_error,
16314        storage,
16315        return_error,
16316        declarator,
16317        body_node,
16318    ] = named.as_slice()
16319    else {
16320        return false;
16321    };
16322    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16323        return false;
16324    };
16325    let Some(constraint_template) = constraint.child_by_field_name("name") else {
16326        return false;
16327    };
16328    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16329        return false;
16330    };
16331    let Some(return_type) = return_error.named_child(0) else {
16332        return false;
16333    };
16334    let mut cursor = constraint_arguments.walk();
16335    let constraint_types = constraint_arguments
16336        .named_children(&mut cursor)
16337        .collect::<Vec<_>>();
16338    same_node(*body_node, body)
16339        && constraint.kind() == "qualified_identifier"
16340        && constraint_scope.kind() == "namespace_identifier"
16341        && constraint_template.kind() == "template_type"
16342        && matches!(constraint_types.as_slice(), [left, right]
16343            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16344        && !constraint_arguments.has_error()
16345        && close_error.kind() == "ERROR"
16346        && close_error.named_child_count() == 0
16347        && storage.kind() == "storage_class_specifier"
16348        && return_error.kind() == "ERROR"
16349        && return_error.named_child_count() == 1
16350        && return_type.kind() == "identifier"
16351        && node_text(return_type, source) == node_text(parameter_name, source)
16352        && extract_function_declarator(*declarator)
16353            .and_then(cpp_function_declarator_name_node)
16354            .is_some()
16355}
16356
16357fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16358    node: Node<'tree>,
16359    parameter_name: Node<'_>,
16360    source: &str,
16361) -> Option<Node<'tree>> {
16362    let body = cpp_reparsed_member_function_body(node)?;
16363    let constraint = node.child_by_field_name("type")?;
16364    let constraint_template = constraint.child_by_field_name("name")?;
16365    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16366    let mut argument_cursor = constraint_arguments.walk();
16367    let constraint_types = constraint_arguments
16368        .named_children(&mut argument_cursor)
16369        .collect::<Vec<_>>();
16370    if constraint.kind() != "qualified_identifier"
16371        || constraint_template.kind() != "template_type"
16372        || !matches!(constraint_types.as_slice(), [left, right]
16373            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16374        || constraint_arguments.has_error()
16375        || node
16376            .child_by_field_name("body")
16377            .is_none_or(|candidate| !same_node(candidate, body))
16378    {
16379        return None;
16380    }
16381
16382    let mut cursor = node.walk();
16383    let recovery_errors = node
16384        .named_children(&mut cursor)
16385        .filter(|child| child.kind() == "ERROR")
16386        .collect::<Vec<_>>();
16387    if !recovery_errors
16388        .iter()
16389        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16390        || !recovery_errors.iter().all(|error| {
16391            error.named_child_count() == 0
16392                || cpp_reparsed_constraint_macro_error(*error, source)
16393                || (error.named_child_count() == 1
16394                    && error
16395                        .named_child(0)
16396                        .is_some_and(|child| child.kind() == "function_declarator"))
16397        })
16398    {
16399        return None;
16400    }
16401
16402    let parameter_text = node_text(parameter_name, source);
16403    let mut declarators = node
16404        .child_by_field_name("declarator")
16405        .and_then(extract_function_declarator)
16406        .into_iter()
16407        .collect::<Vec<_>>();
16408    for error in recovery_errors {
16409        let mut stack = vec![error];
16410        while let Some(current) = stack.pop() {
16411            if current.kind() == "function_declarator" {
16412                declarators.push(current);
16413            }
16414            let mut cursor = current.walk();
16415            stack.extend(current.named_children(&mut cursor));
16416        }
16417    }
16418    declarators.into_iter().find(|declarator| {
16419        cpp_function_declarator_name_node(*declarator)
16420            .is_some_and(|name| name.kind() == "identifier")
16421            && declarator
16422                .child_by_field_name("parameters")
16423                .is_some_and(|parameters| {
16424                    parameters
16425                        .named_children(&mut parameters.walk())
16426                        .filter_map(|parameter| parameter.child_by_field_name("type"))
16427                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
16428                })
16429    })
16430}
16431
16432fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
16433    node: Node<'_>,
16434    parameter_name: Node<'_>,
16435    source: &str,
16436) -> bool {
16437    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
16438}
16439
16440fn cpp_reparsed_template_macro_function_companion_is_indexable(
16441    node: Node<'_>,
16442    parameter_name: Node<'_>,
16443    source: &str,
16444) -> bool {
16445    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
16446        return false;
16447    }
16448    let Some(return_type) = node.child_by_field_name("type") else {
16449        return false;
16450    };
16451    let Some(function_declarator) = node
16452        .child_by_field_name("declarator")
16453        .and_then(extract_function_declarator)
16454    else {
16455        return false;
16456    };
16457    if cpp_function_declarator_name_node(function_declarator).is_none()
16458        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
16459    {
16460        return false;
16461    }
16462    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
16463        return false;
16464    };
16465    let parameter_text = node_text(parameter_name, source);
16466    parameters
16467        .named_children(&mut parameters.walk())
16468        .any(|parameter| {
16469            parameter
16470                .child_by_field_name("type")
16471                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
16472        })
16473}
16474
16475fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
16476    if node.kind() != "ERROR" {
16477        return false;
16478    }
16479    let mut stack = vec![node];
16480    while let Some(current) = stack.pop() {
16481        let macro_shape = match current.kind() {
16482            "call_expression" => current
16483                .child_by_field_name("function")
16484                .zip(current.child_by_field_name("arguments")),
16485            "init_declarator" => current
16486                .child_by_field_name("declarator")
16487                .zip(current.child_by_field_name("value")),
16488            _ => None,
16489        };
16490        if let Some((name, arguments)) = macro_shape
16491            && name.kind() == "identifier"
16492            && arguments.kind() == "argument_list"
16493            && arguments.named_child_count() >= 2
16494            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16495        {
16496            return true;
16497        }
16498        let mut cursor = current.walk();
16499        stack.extend(current.named_children(&mut cursor));
16500    }
16501    false
16502}
16503
16504fn cpp_recovered_template_macro_constructor<'tree>(
16505    node: Node<'tree>,
16506    source: &str,
16507) -> Option<(Node<'tree>, Node<'tree>)> {
16508    let mut prefix = node.prev_named_sibling()?;
16509    while prefix.kind() == "comment" {
16510        prefix = prefix.prev_named_sibling()?;
16511    }
16512    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
16513    let parameter = parameter_name
16514        .parent()
16515        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
16516    let declarator =
16517        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
16518    Some((declarator, parameter))
16519}
16520
16521fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
16522    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
16523        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16524            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
16525                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
16526                    function,
16527                    parameter_name,
16528                    source,
16529                )
16530        });
16531    }
16532    let Some(parameter_name) =
16533        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
16534    else {
16535        return false;
16536    };
16537    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
16538        cpp_reparsed_template_macro_function_companion_is_indexable(
16539            function,
16540            parameter_name,
16541            source,
16542        )
16543    })
16544}
16545
16546fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16547    let function_name = node
16548        .child_by_field_name("declarator")
16549        .and_then(extract_function_declarator)
16550        .and_then(cpp_function_declarator_name_node);
16551    if let Some(body) = cpp_reparsed_member_function_body(node)
16552        && function_name.is_some()
16553        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
16554    {
16555        return true;
16556    }
16557    cpp_reparsed_attribute_member_function(node, source)
16558        || cpp_reparsed_friend_function_is_indexable(node, source)
16559        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
16560        || cpp_reparsed_access_template_function_is_indexable(node, source)
16561        || cpp_recovered_template_macro_constructor(node, source).is_some()
16562}
16563
16564/// Recognize the three top-level nodes produced when an unknown attribute
16565/// macro separates an inline member's declarator from its body in a reparsed
16566/// class interior: an errorful declaration with a missing semicolon, the macro
16567/// call expression, and the complete compound body. Their adjacency and exact
16568/// structured shapes prove one recoverable member envelope; arbitrary calls or
16569/// blocks do not pass this gate.
16570fn cpp_reparsed_macro_attribute_member_sequence(
16571    children: &[Node<'_>],
16572    index: usize,
16573    source: &str,
16574) -> bool {
16575    let Some(prefix) = children.get(index).copied() else {
16576        return false;
16577    };
16578    let declaration = if prefix.kind() == "labeled_statement" {
16579        prefix
16580            .named_child(prefix.named_child_count().saturating_sub(1))
16581            .filter(|child| child.kind() == "declaration")
16582    } else {
16583        (prefix.kind() == "declaration").then_some(prefix)
16584    };
16585    let Some(declaration) = declaration else {
16586        return false;
16587    };
16588    if !declaration.has_error()
16589        || declaration
16590            .child_by_field_name("declarator")
16591            .and_then(extract_function_declarator)
16592            .and_then(cpp_function_declarator_name_node)
16593            .is_none()
16594    {
16595        return false;
16596    }
16597    let Some(attribute_statement) = children.get(index + 1).copied() else {
16598        return false;
16599    };
16600    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
16601        .then(|| attribute_statement.named_child(0))
16602        .flatten()
16603        .filter(|child| child.kind() == "call_expression")
16604    else {
16605        return false;
16606    };
16607    let Some(attribute_name) = attribute_call
16608        .child_by_field_name("function")
16609        .filter(|function| function.kind() == "identifier")
16610        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
16611    else {
16612        return false;
16613    };
16614    if !cpp_export_macro_token(&attribute_name) {
16615        return false;
16616    }
16617    let Some(body) = children.get(index + 2).copied() else {
16618        return false;
16619    };
16620    body.kind() == "compound_statement"
16621        && body.child(0).is_some_and(|open| open.kind() == "{")
16622        && body
16623            .child(body.child_count().saturating_sub(1))
16624            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
16625        && declaration.end_byte() <= attribute_statement.start_byte()
16626        && attribute_statement.end_byte() <= body.start_byte()
16627}
16628
16629/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
16630/// types and specifiers followed by a declarator, over and over, with nothing
16631/// left over. A string-argument attribute macro is what strands them there, and
16632/// the walk indexes exactly what this reads, so admitting the region is safe
16633/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
16634/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
16635/// the whole class body.
16636fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
16637    if node.kind() != "ERROR" {
16638        return false;
16639    }
16640    let run = stranded_declaration_run(node, source);
16641    run.complete && !run.declarations.is_empty()
16642}
16643
16644fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
16645    let mut cursor = root.walk();
16646    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
16647    let mut saw_member = false;
16648    let mut index = 0;
16649    while index < children.len() {
16650        let child = children[index];
16651        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
16652            saw_member = true;
16653            index += 3;
16654            continue;
16655        }
16656        if let Some(recovered) = fragmented_class_body(child, source) {
16657            let Some(tree) = cpp_reparse_fragmented_class_body(
16658                source,
16659                recovered.body.reparse_start,
16660                recovered.body.reparse_end,
16661            ) else {
16662                return false;
16663            };
16664            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
16665                return false;
16666            }
16667            saw_member = true;
16668            index += 1;
16669            while index < children.len()
16670                && children[index].end_byte() <= recovered.body.class_range.end_byte
16671            {
16672                index += 1;
16673            }
16674            continue;
16675        }
16676        match child.kind() {
16677            "comment" => {}
16678            "labeled_statement" => saw_member = true,
16679            "function_definition" => {
16680                if child.has_error()
16681                    && !cpp_reparsed_member_function_is_indexable(child, source)
16682                    && cpp_sentinel_macro_region(child, source).is_none()
16683                {
16684                    return false;
16685                }
16686                saw_member = true;
16687            }
16688            // The attribute a string-argument macro leaves as a call statement
16689            // of its own. It declares nothing; the member it decorated is the
16690            // sibling after it, checked on its own turn.
16691            "expression_statement" if is_string_attribute_macro_statement(child) => {}
16692            "ERROR"
16693                if (cpp_reparsed_member_error_is_indexable(child)
16694                    || cpp_reparsed_adjacent_copy_control_error(child, source)
16695                    || cpp_reparsed_stranded_member_error(child, source))
16696                    && (child
16697                        .next_named_sibling()
16698                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
16699                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
16700            {
16701                saw_member = true;
16702            }
16703            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
16704                saw_member = true;
16705            }
16706            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
16707                saw_member = true;
16708            }
16709            "expression_statement"
16710                if cpp_is_stray_semicolon(child, source)
16711                    && child.prev_named_sibling().is_some_and(|error| {
16712                        cpp_reparsed_member_error_is_indexable(error)
16713                            || cpp_reparsed_adjacent_copy_control_error(error, source)
16714                            || cpp_reparsed_stranded_member_error(error, source)
16715                    }) =>
16716            {
16717                saw_member = true;
16718            }
16719            "compound_statement"
16720                if cpp_reparsed_constructor_body_is_indexable(child, source)
16721                    || cpp_reparsed_attribute_requires_body(child, source) =>
16722            {
16723                saw_member = true;
16724            }
16725            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
16726            _ => return false,
16727        }
16728        index += 1;
16729    }
16730    saw_member
16731}
16732
16733/// Detect the malformed constructor shape that tree-sitter exposes as an
16734/// access-label statement followed by initializer-looking declarations. The
16735/// declarations are not class members: visiting their `location(loc)` and
16736/// `string(s)` function declarators would publish synthetic functions. The
16737/// export-class fallback keeps the original sibling nodes and therefore avoids
16738/// this parser artifact. The returned range identifies the real constructor
16739/// header, which can be reparsed independently as a structured declarator.
16740fn cpp_reparsed_synthetic_initializer_constructor_range(
16741    root: Node<'_>,
16742    class_name: &str,
16743    source: &str,
16744    constructor_end: usize,
16745) -> Option<std::ops::Range<usize>> {
16746    let mut stack = {
16747        let mut cursor = root.walk();
16748        root.named_children(&mut cursor).collect::<Vec<_>>()
16749    };
16750    while let Some(current) = stack.pop() {
16751        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
16752            current,
16753            class_name,
16754            source,
16755            constructor_end,
16756        ) {
16757            return Some(range);
16758        }
16759        if current.kind() == "ERROR" {
16760            let mut cursor = current.walk();
16761            stack.extend(current.named_children(&mut cursor));
16762        }
16763    }
16764    None
16765}
16766
16767/// Recover an inline constructor that a function-like export macro makes
16768/// tree-sitter merge with the following overload. In the reparsed class-body
16769/// region, the access label wraps one declaration whose ERROR contains the
16770/// constructor declarator and its base-initializer/body, while the declaration's
16771/// ordinary declarator is the following overload. Every boundary below comes
16772/// from that CST; no source syntax is reparsed by hand.
16773fn cpp_reparsed_merged_inline_constructor<'tree>(
16774    root: Node<'tree>,
16775    class_name: &str,
16776    source: &str,
16777) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
16778    let mut stack = vec![root];
16779    while let Some(current) = stack.pop() {
16780        if current.kind() != "labeled_statement" {
16781            let mut cursor = current.walk();
16782            stack.extend(current.named_children(&mut cursor));
16783            continue;
16784        }
16785        let declaration = current
16786            .named_children(&mut current.walk())
16787            .find(|child| child.kind() == "declaration")?;
16788        if declaration
16789            .child_by_field_name("type")
16790            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
16791        {
16792            continue;
16793        }
16794        let following = declaration
16795            .child_by_field_name("declarator")
16796            .and_then(extract_function_declarator)
16797            .and_then(cpp_function_declarator_name_node);
16798        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
16799            continue;
16800        }
16801        let mut declaration_cursor = declaration.walk();
16802        let Some(error) = declaration
16803            .named_children(&mut declaration_cursor)
16804            .find(|child| child.kind() == "ERROR")
16805        else {
16806            continue;
16807        };
16808        let mut error_cursor = error.walk();
16809        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
16810        let Some(constructor) = error_children.iter().copied().find(|child| {
16811            child.kind() == "function_declarator"
16812                && cpp_function_declarator_name_node(*child)
16813                    .is_some_and(|name| node_text(name, source).trim() == class_name)
16814        }) else {
16815            continue;
16816        };
16817        let Some(body) = error_children.iter().copied().find_map(|child| {
16818            (child.kind() == "init_declarator")
16819                .then(|| child.child_by_field_name("value"))
16820                .flatten()
16821                .filter(|value| value.kind() == "initializer_list")
16822        }) else {
16823            continue;
16824        };
16825        if constructor.end_byte() > body.start_byte() {
16826            continue;
16827        }
16828        return Some((constructor.start_byte()..body.end_byte(), body));
16829    }
16830    None
16831}
16832
16833fn cpp_reparsed_synthetic_initializer_constructor(
16834    node: Node<'_>,
16835    class_name: &str,
16836    source: &str,
16837    constructor_end: usize,
16838) -> Option<std::ops::Range<usize>> {
16839    if node.kind() != "labeled_statement" {
16840        return None;
16841    }
16842    let mut cursor = node.walk();
16843    let named = node
16844        .named_children(&mut cursor)
16845        .filter(|child| child.kind() != "comment")
16846        .collect::<Vec<_>>();
16847    let label = named.first()?;
16848    if label.kind() != "statement_identifier"
16849        || !matches!(
16850            node_text(*label, source).trim(),
16851            "public" | "private" | "protected"
16852        )
16853    {
16854        return None;
16855    }
16856    let call_error_index = named.iter().position(|child| {
16857        if child.kind() != "ERROR" {
16858            return false;
16859        }
16860        let mut stack = vec![*child];
16861        while let Some(current) = stack.pop() {
16862            if current.kind() == "call_expression"
16863                && current
16864                    .child_by_field_name("function")
16865                    .is_some_and(|function| {
16866                        function.kind() == "identifier"
16867                            && node_text(function, source).trim() == class_name
16868                    })
16869            {
16870                return true;
16871            }
16872            let mut cursor = current.walk();
16873            stack.extend(current.named_children(&mut cursor));
16874        }
16875        false
16876    })?;
16877    let constructor_call = {
16878        let mut stack = vec![named[call_error_index]];
16879        let mut found = None;
16880        while let Some(current) = stack.pop() {
16881            if current.kind() == "call_expression"
16882                && current
16883                    .child_by_field_name("function")
16884                    .is_some_and(|function| {
16885                        function.kind() == "identifier"
16886                            && node_text(function, source).trim() == class_name
16887                    })
16888            {
16889                found = Some(current);
16890                break;
16891            }
16892            let mut cursor = current.walk();
16893            stack.extend(current.named_children(&mut cursor));
16894        }
16895        found
16896    };
16897    let constructor_call = constructor_call?;
16898    named.iter().skip(call_error_index + 1).find(|child| {
16899        child.kind() == "declaration" && child.has_error() && {
16900            let mut cursor = child.walk();
16901            child.named_children(&mut cursor).any(|declarator| {
16902                declarator.kind() == "init_declarator"
16903                    && declarator
16904                        .child_by_field_name("declarator")
16905                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
16906                    && declarator
16907                        .child_by_field_name("value")
16908                        .is_some_and(|value| value.kind() == "initializer_list")
16909            })
16910        }
16911    })?;
16912    Some(constructor_call.start_byte()..constructor_end)
16913}
16914
16915fn cpp_reparsed_exact_constructor_declarator<'tree>(
16916    root: Node<'tree>,
16917    start: usize,
16918    class_name: &str,
16919    source: &str,
16920) -> Option<Node<'tree>> {
16921    let mut candidate = None;
16922    let mut stack = vec![root];
16923    while let Some(current) = stack.pop() {
16924        if current.kind() == "function_declarator"
16925            && current.start_byte() == start
16926            && cpp_function_declarator_name_node(current)
16927                .is_some_and(|name| node_text(name, source).trim() == class_name)
16928        {
16929            if candidate.is_some() {
16930                return None;
16931            }
16932            candidate = Some(current);
16933            continue;
16934        }
16935        let mut cursor = current.walk();
16936        stack.extend(current.named_children(&mut cursor));
16937    }
16938    candidate
16939}
16940
16941fn cpp_is_indexable_item_kind(kind: &str) -> bool {
16942    matches!(
16943        kind,
16944        "namespace_definition"
16945            | "class_specifier"
16946            | "struct_specifier"
16947            | "union_specifier"
16948            | "enum_specifier"
16949            | "function_definition"
16950            | "template_declaration"
16951            | "declaration"
16952            | "field_declaration"
16953            | "alias_declaration"
16954            | "static_assert_declaration"
16955            | "type_definition"
16956            | "using_declaration"
16957            | "linkage_specification"
16958            | "preproc_def"
16959            | "preproc_function_def"
16960            | "preproc_include"
16961            | "preproc_if"
16962            | "preproc_ifdef"
16963            | "preproc_call"
16964    )
16965}
16966
16967#[cfg(test)]
16968mod tests {
16969    use super::*;
16970    use crate::adapter::parse_cpp_file;
16971    use brokk_bifrost_core::analyzer::parsed_file::{
16972        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
16973        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
16974    };
16975    use std::fmt::Write;
16976
16977    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
16978        let mut parser = tree_sitter::Parser::new();
16979        parser
16980            .set_language(&tree_sitter_cpp::LANGUAGE.into())
16981            .unwrap();
16982        let tree = parser.parse(source, None).unwrap();
16983        let file = ProjectFile::new(std::env::temp_dir(), name);
16984        parse_cpp_file(&file, source, &tree)
16985    }
16986
16987    #[test]
16988    fn displaced_terminator_ignores_nested_initializer_endif() {
16989        let source = "#ifdef ENABLE_ITEMS\n\
16990struct Item { int reg; };\n\
16991static const struct Item items[] = {{1}};\n\
16992static const struct Item extras[] = {\n\
16993  {0},\n\
16994#ifndef REDUCED\n\
16995  {1},\n\
16996#endif\n\
16997};\n\
16998int read_item(int i) { return items[i].reg; }\n\
16999#endif\n";
17000        let mut parser = tree_sitter::Parser::new();
17001        parser
17002            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17003            .expect("C++ grammar");
17004        let tree = parser.parse(source, None).expect("fixture tree");
17005        let conditional = tree
17006            .root_node()
17007            .named_child(0)
17008            .filter(|node| node.kind() == "preproc_ifdef")
17009            .expect("outer conditional");
17010
17011        assert!(conditional.has_error());
17012        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17013        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17014    }
17015
17016    #[test]
17017    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17018        let source = "struct Image { PyObject_HEAD Imaging image; };";
17019        let parsed = parse_cpp_declarations(source, "image.h");
17020        let names = parsed
17021            .declarations()
17022            .iter()
17023            .map(|unit| unit.fq_name())
17024            .collect::<Vec<_>>();
17025
17026        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17027        assert!(
17028            names.iter().all(|name| name != "Image.Imaging"),
17029            "the pseudo-declarator must not become a field: {names:#?}"
17030        );
17031
17032        let pointer = parse_cpp_declarations(
17033            "struct Image { PyObject_HEAD Imaging *image; };",
17034            "image-pointer.h",
17035        );
17036        let pointer_names = pointer
17037            .declarations()
17038            .iter()
17039            .map(|unit| unit.fq_name())
17040            .collect::<Vec<_>>();
17041        assert!(
17042            pointer_names.iter().any(|name| name == "Image.image"),
17043            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17044        );
17045        assert!(
17046            pointer_names.iter().all(|name| name != "Image.Imaging"),
17047            "the pointer recovery error must not become a field: {pointer_names:#?}"
17048        );
17049
17050        let unrelated_macro = parse_cpp_declarations(
17051            "struct Image { OTHER_HEAD Imaging other; };",
17052            "image-near-miss.h",
17053        );
17054        let unrelated_names = unrelated_macro
17055            .declarations()
17056            .iter()
17057            .map(|unit| unit.fq_name())
17058            .collect::<Vec<_>>();
17059        assert!(
17060            unrelated_names.iter().all(|name| name != "Image.other"),
17061            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17062        );
17063    }
17064
17065    #[test]
17066    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17067        let source = r#"namespace testing {
17068namespace internal {
17069    namespace detail {
17070        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17071        public:
17072            int value() const { return count_ + 1; }
17073        private:
17074            int count_;
17075        };
17076        class GTEST_API_ [[nodiscard]] OtherReporter {
17077        public:
17078            int value() const { return count_ + 2; }
17079        private:
17080            int count_;
17081        };
17082    }
17083
17084    template <typename T>
17085    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17086
17087    class TailReporter {};
17088}
17089}
17090"#;
17091        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17092        let declarations = parsed.declarations();
17093        let mut parser = tree_sitter::Parser::new();
17094        parser
17095            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17096            .unwrap();
17097        let tree = parser.parse(source, None).unwrap();
17098        let tail_start = source.find("TailReporter").expect("tail class");
17099        let tail_node = tree
17100            .root_node()
17101            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17102            .expect("tail class AST node");
17103        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17104        assert!(
17105            tree.root_node().has_error(),
17106            "the malformed class must exercise recovery"
17107        );
17108        assert!(index.region_at(tail_start).is_some());
17109        assert_eq!(
17110            index.enclosing_namespace_components(tail_node, source),
17111            ["testing", "internal"]
17112        );
17113        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17114        let mut recovered_parsed = ParsedFile::new(String::new());
17115        let class_unit = CodeUnit::new_fq(
17116            file.clone(),
17117            CodeUnitType::Class,
17118            "testing",
17119            "ScopedFakeTestPartResultReporter",
17120            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17121        );
17122        let scope = ScopeInfo {
17123            package_name: "testing".to_string(),
17124            module: None,
17125            class_unit: Some(class_unit),
17126            template_signature: Some("<typename T>".to_string()),
17127            template_metadata: Some(CppTemplateMetadata {
17128                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17129                primary_fq_name: String::new(),
17130                parameters: Vec::new(),
17131                specialization_arguments: Vec::new(),
17132                alias_target: None,
17133            }),
17134            declarations_are_fields: true,
17135            recovered_specialization_member_scope: true,
17136            visible_using_namespaces: Vec::new(),
17137        };
17138        let mut visitor = CppVisitor {
17139            file: &file,
17140            source,
17141            parsed: &mut recovered_parsed,
17142            c_tag_semantics: false,
17143            recovered_class_sibling_scopes: HashMap::default(),
17144            consumed_fragment_regions: Vec::new(),
17145            orphaned_namespaces: index,
17146            partitioned_regions: Vec::new(),
17147            namespace_forward_scans: HashMap::default(),
17148            field_owners: None,
17149            recovery_captures: Vec::new(),
17150            object_macro_fields: HashMap::default(),
17151            ambiguous_object_macro_fields: HashSet::default(),
17152        };
17153        let recovered = visitor
17154            .recovered_namespace_scope(tail_node, &scope)
17155            .expect("the tail must use the stolen namespace scope");
17156        assert_eq!(recovered.package_name, "testing::internal");
17157        assert!(
17158            recovered.class_unit.is_none(),
17159            "recovered namespace declarations cannot retain the malformed class owner"
17160        );
17161        assert!(recovered.template_signature.is_none());
17162        assert!(recovered.template_metadata.is_none());
17163        assert!(!recovered.declarations_are_fields);
17164        assert!(!recovered.recovered_specialization_member_scope);
17165        assert!(
17166            declarations
17167                .iter()
17168                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17169            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17170        );
17171        assert!(
17172            declarations
17173                .iter()
17174                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17175            "the recovered free function remains in its namespace: {declarations:#?}"
17176        );
17177        assert!(
17178            declarations
17179                .iter()
17180                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17181            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17182        );
17183        assert!(
17184            declarations.iter().all(|unit| {
17185                !unit
17186                    .short_name()
17187                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17188            }),
17189            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17190        );
17191        assert!(
17192            declarations
17193                .iter()
17194                .all(|unit| !unit.identifier().is_empty()),
17195            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17196        );
17197    }
17198
17199    #[test]
17200    fn object_like_field_macros_materialize_owner_specific_declarations() {
17201        let source = r#"#define PUBLIC_FIELDS int public_value;
17202#define PRIVATE_FIELDS int private_value;
17203#define NOT_A_FIELD_LIST not a declaration
17204
17205struct First {
17206  PUBLIC_FIELDS
17207  PRIVATE_FIELDS
17208};
17209struct Second {
17210  PUBLIC_FIELDS
17211  NOT_A_FIELD_LIST
17212};
17213#undef PUBLIC_FIELDS
17214struct Third {
17215  PUBLIC_FIELDS
17216};
17217"#;
17218        let parsed = parse_cpp_declarations(source, "macro-fields.c");
17219        let fields = parsed
17220            .declarations()
17221            .iter()
17222            .filter(|unit| unit.is_field())
17223            .map(|unit| unit.fq_name())
17224            .collect::<Vec<_>>();
17225
17226        assert!(
17227            fields.contains(&"First.public_value".to_string()),
17228            "{fields:?}"
17229        );
17230        assert!(
17231            fields.contains(&"First.private_value".to_string()),
17232            "{fields:?}"
17233        );
17234        assert!(
17235            fields.contains(&"Second.public_value".to_string()),
17236            "{fields:?}"
17237        );
17238        assert!(
17239            !fields.iter().any(|field| field.contains("not_a_field")),
17240            "malformed macro must fail closed: {fields:?}"
17241        );
17242        assert!(
17243            !fields.iter().any(|field| field.starts_with("Third.")),
17244            "undefined macro must fail closed: {fields:?}"
17245        );
17246    }
17247
17248    #[test]
17249    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17250        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17251        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17252        let mut macros = parsed
17253            .declarations()
17254            .iter()
17255            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17256            .collect::<Vec<_>>();
17257        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17258
17259        assert_eq!(macros.len(), 2, "{macros:#?}");
17260        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17261        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17262        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17263        assert_eq!(
17264            parsed.declaration_ranges(macros[1])[0].start_byte,
17265            source.rfind("#define VALUE 2").expect("second definition")
17266        );
17267    }
17268
17269    #[test]
17270    fn identifies_export_macro_class_base_displaced_into_declarator() {
17271        let source = r#"#define PROJECT_API_
17272namespace project {
17273namespace internal {
17274template <typename T>
17275class Base {};
17276}
17277template <typename T>
17278class Wrapper;
17279template <>
17280class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17281}
17282"#;
17283        let mut parser = tree_sitter::Parser::new();
17284        parser
17285            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17286            .unwrap();
17287        let tree = parser.parse(source, None).unwrap();
17288        let start = source.find("internal::Base<int>").expect("base");
17289        let mut base = tree
17290            .root_node()
17291            .descendant_for_byte_range(start, start + 8)
17292            .expect("base syntax");
17293        while base.kind() != "qualified_identifier" {
17294            base = base.parent().expect("qualified base ancestor");
17295        }
17296        assert!(
17297            is_recovered_exported_class_base_type_node(base, source),
17298            "{}",
17299            tree.root_node().to_sexp()
17300        );
17301    }
17302
17303    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17304        let mut identities = parsed
17305            .declarations()
17306            .iter()
17307            .filter(|unit| unit.is_function())
17308            .map(|unit| {
17309                (
17310                    unit.fq_name(),
17311                    unit.signature().unwrap_or_default().to_string(),
17312                )
17313            })
17314            .collect::<Vec<_>>();
17315        identities.sort();
17316        identities
17317    }
17318
17319    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
17320    /// prototype macros. Each case is parsed independently so a preceding
17321    /// malformed line cannot flatten the next declaration's fields into one
17322    /// cascading `ERROR`; that shape has no structural proof and must fail
17323    /// closed rather than fall back to a token scan. Parameter names are
17324    /// dropped from signatures just as they are for ordinary C prototypes.
17325    #[test]
17326    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17327        let cases = [
17328            (
17329                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17330                "pg_typemap_fit_to_result",
17331                "(VALUE, VALUE)",
17332            ),
17333            (
17334                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17335                "pg_typemap_result_value",
17336                "(t_typemap *, VALUE, int, int)",
17337            ),
17338            (
17339                "void pg_typemap_mark _(( void * ));",
17340                "pg_typemap_mark",
17341                "(void *)",
17342            ),
17343            (
17344                "void init_pg_type_map _(( void ));",
17345                "init_pg_type_map",
17346                "(void)",
17347            ),
17348            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
17349            (
17350                "extern VALUE pg_extern _(( VALUE, VALUE ));",
17351                "pg_extern",
17352                "(VALUE, VALUE)",
17353            ),
17354            (
17355                "size_t pg_typemap_memsize _(( const void * ));",
17356                "pg_typemap_memsize",
17357                "(const void *)",
17358            ),
17359            (
17360                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
17361                "pg_wrap_socket_io",
17362                "(int, VALUE, VALUE *, int *)",
17363            ),
17364            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
17365            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
17366            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
17367        ];
17368
17369        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
17370            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
17371            assert_eq!(
17372                function_identities(&parsed),
17373                vec![(name.to_string(), signature.to_string())],
17374                "{source}: {:#?}",
17375                parsed.declarations()
17376            );
17377            assert!(
17378                parsed.declarations().iter().all(|unit| !unit.is_field()),
17379                "{source} must not retain the malformed field: {:#?}",
17380                parsed.declarations()
17381            );
17382        }
17383    }
17384
17385    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
17386    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
17387    /// it as a multiplication of a type by a call
17388    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
17389    /// wrapped in an `expression_statement` that `has_error()` only because
17390    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
17391    /// here with a clean preceding field so the prototype remains the
17392    /// parser-owned `expression_statement` this recovery requires.
17393    #[test]
17394    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
17395        let cases = [
17396            (
17397                "PGconn *pg_get_pgconn _(( VALUE ));",
17398                "pg_get_pgconn",
17399                "(VALUE)",
17400            ),
17401            (
17402                "PGresult* pgresult_get _(( VALUE ));",
17403                "pgresult_get",
17404                "(VALUE)",
17405            ),
17406        ];
17407        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
17408            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
17409            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
17410            assert_eq!(
17411                function_identities(&parsed),
17412                vec![(name.to_string(), signature.to_string())],
17413                "{source}: {:#?}",
17414                parsed.declarations()
17415            );
17416            assert_eq!(
17417                parsed
17418                    .declarations()
17419                    .iter()
17420                    .filter(|unit| unit.is_field())
17421                    .map(|unit| unit.fq_name())
17422                    .collect::<Vec<_>>(),
17423                vec!["rb_mPG".to_string()],
17424                "{source}: {:#?}",
17425                parsed.declarations()
17426            );
17427        }
17428    }
17429
17430    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
17431    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
17432    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
17433    /// regions. The witnessed declaration must still become a Function and
17434    /// its type must not survive as the name of a bogus Field. This test does
17435    /// not claim that structurally flattened neighboring prototypes are safe
17436    /// to recover.
17437    #[test]
17438    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
17439        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
17440VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
17441int pg_typemap_fit_to_copy_get                         _(( VALUE ));
17442VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
17443t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
17444VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
17445void pg_typemap_mark                                   _(( void * ));
17446size_t pg_typemap_memsize                              _(( const void * ));
17447void pg_typemap_compact                                _(( void * ));
17448
17449PGconn *pg_get_pgconn                                  _(( VALUE ));
17450t_pg_connection *pg_get_connection                     _(( VALUE ));
17451VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
17452#ifdef __GNUC__
17453__attribute__((format(printf, 3, 4)))
17454#endif
17455NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
17456VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
17457void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
17458
17459
17460VALUE pg_new_result                                    _(( PGresult *, VALUE ));
17461VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
17462PGresult* pgresult_get                                 _(( VALUE ));
17463VALUE pg_result_check                                  _(( VALUE ));
17464VALUE pg_result_clear                                  _(( VALUE ));
17465VALUE pg_tuple_new                                     _(( VALUE, int ));
17466
17467/*
17468 * Fetch the data pointer for the result object
17469 */
17470static inline t_pg_result *
17471pgresult_get_this( VALUE self )
17472{
17473	return RTYPEDDATA_DATA(self);
17474}
17475
17476
17477rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
17478const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
17479rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
17480
17481"#;
17482        let parsed = parse_cpp_declarations(source, "pg.h");
17483        assert!(
17484            function_identities(&parsed)
17485                .iter()
17486                .any(|(name, signature)| name == "pg_typemap_result_value"
17487                    && signature == "(t_typemap *, VALUE, int, int)"),
17488            "the real issue witness must be a Function with its C signature: {:#?}",
17489            parsed.declarations()
17490        );
17491        assert!(
17492            parsed
17493                .declarations()
17494                .iter()
17495                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
17496            "the issue witness must not leave its return type as a Field name: {:#?}",
17497            parsed.declarations()
17498        );
17499    }
17500
17501    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
17502    /// between `explicit` and a constructor name onto the name, producing a
17503    /// `qualified_identifier` whose `::` it had to invent. The declared member
17504    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
17505    /// macro in type position, was already recovered and is the control that
17506    /// both spellings agree.
17507    #[test]
17508    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
17509        let source = r#"class SIMD_4x26 final {
17510   public:
17511      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
17512      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
17513      int m_v;
17514};
17515"#;
17516        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
17517        assert_eq!(
17518            function_identities(&parsed),
17519            vec![
17520                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
17521                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
17522            ],
17523            "{:#?}",
17524            parsed.declarations()
17525        );
17526    }
17527
17528    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
17529    /// `ERROR` holding the wrapped declaration and every declaration after it
17530    /// until the parser recovers. All of them must be indexed, each with the
17531    /// byte range that spells it.
17532    #[test]
17533    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
17534        let source = r#"#include <cstdint>
17535struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
17536    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
17537                     struct llama_model * model,
17538              struct llama_context_params   params),
17539            "use llama_init_from_model instead");
17540    LLAMA_API int32_t llama_tokenize(
17541        const struct llama_vocab * vocab,
17542                      const char * text,
17543                            bool   parse_special);
17544    LLAMA_API int32_t llama_other(int a);
17545"#;
17546        let parsed = parse_cpp_declarations(source, "llama.h");
17547        assert_eq!(
17548            function_identities(&parsed),
17549            vec![
17550                (
17551                    "llama_new_context_with_model".to_string(),
17552                    "(struct llama_model *, struct llama_context_params)".to_string()
17553                ),
17554                ("llama_other".to_string(), "(int)".to_string()),
17555                (
17556                    "llama_tokenize".to_string(),
17557                    "(const struct llama_vocab *, const char *, bool)".to_string()
17558                ),
17559            ],
17560            "{:#?}",
17561            parsed.declarations()
17562        );
17563
17564        // Each recovered declaration owns the source that spells it, so
17565        // navigation lands on the declaration and not on the macro envelope.
17566        for (name, expected) in [
17567            (
17568                "llama_new_context_with_model",
17569                "LLAMA_API struct llama_context * llama_new_context_with_model(",
17570            ),
17571            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
17572            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
17573        ] {
17574            let unit = parsed
17575                .declarations()
17576                .iter()
17577                .find(|unit| unit.is_function() && unit.fq_name() == name)
17578                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
17579            let [range] = parsed.declaration_ranges(unit) else {
17580                panic!("{name} must have exactly one range");
17581            };
17582            let text = &source[range.start_byte..range.end_byte];
17583            assert!(
17584                text.starts_with(expected),
17585                "{name} range is {text:?}, expected it to start with {expected:?}"
17586            );
17587            assert!(
17588                text.ends_with(')') || text.ends_with(';'),
17589                "{name}: {text:?}"
17590            );
17591        }
17592    }
17593
17594    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
17595    /// invocation's `(` at the end of its line, and the parser then keeps no
17596    /// invocation whole. It hands the first one's hint and its own `)` and `;`
17597    /// to the `expression_statement` beside it, and flattens the ones after
17598    /// that into bare `identifier`/`type_identifier`, `(`,
17599    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
17600    /// already inside. No part of that is a head the grouping can be read from,
17601    /// which is why the invocation is read from the token order instead
17602    /// (#3094).
17603    ///
17604    /// The identifiers are `library_*` at whisper's own lengths on purpose:
17605    /// tree-sitter prices error recovery partly by how much input a repair
17606    /// skips, and the same construct written with shorter names parses cleanly.
17607    #[test]
17608    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
17609        let source = r#"    LIBRARY_DEPRECATED(
17610        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17611        "use library_init_from_file_with_params instead"
17612    );
17613    LIBRARY_DEPRECATED(
17614        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
17615        "use library_init_from_buffer_with_params instead"
17616    );
17617    LIBRARY_DEPRECATED(
17618        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
17619        "use library_init_with_params instead"
17620    );
17621"#;
17622        let mut parser = tree_sitter::Parser::new();
17623        parser
17624            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17625            .expect("C++ grammar");
17626        let tree = parser.parse(source, None).expect("C++ tree");
17627        let root = tree.root_node();
17628
17629        // The shape this recovery exists for: the second invocation is not a
17630        // node of its own, and the third is a run of bare siblings inside the
17631        // second one's `ERROR`.
17632        let mut cursor = root.walk();
17633        let items = root.children(&mut cursor).collect::<Vec<_>>();
17634        let [first, hint_statement, rest @ ..] = items.as_slice() else {
17635            panic!("{}", root.to_sexp());
17636        };
17637        assert_eq!(first.kind(), "ERROR");
17638        assert_eq!(hint_statement.kind(), "expression_statement");
17639        assert!(
17640            node_text(*first, source).ends_with(','),
17641            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
17642            root.to_sexp()
17643        );
17644        let swallowing = rest
17645            .iter()
17646            .find(|item| item.kind() == "ERROR")
17647            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
17648        let mut swallowing_cursor = swallowing.walk();
17649        let flattened = swallowing
17650            .children(&mut swallowing_cursor)
17651            .map(|child| child.kind())
17652            .collect::<Vec<_>>();
17653        assert_eq!(
17654            flattened,
17655            vec![
17656                "identifier",
17657                "(",
17658                "parameter_declaration",
17659                ",",
17660                "ERROR",
17661                "type_identifier",
17662                "(",
17663                "parameter_declaration",
17664                ",",
17665                "\"",
17666                "identifier",
17667                "identifier",
17668                "identifier",
17669                "\"",
17670                ")",
17671            ],
17672            "the third invocation must be flattened into the second one's node: {}",
17673            root.to_sexp()
17674        );
17675
17676        // Both are admitted, and each names the byte just past its own `;`.
17677        let ancestry = ParentIndex::new(root);
17678        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
17679            .expect("the first invocation");
17680        assert_eq!(
17681            &source[..first_run.invocation_end],
17682            &source[..source.find("instead\"\n    );").expect("first hint")
17683                + "instead\"\n    );".len()]
17684        );
17685        assert_eq!(
17686            first_run.region_end, first_run.invocation_end,
17687            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
17688        );
17689        let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
17690            .expect("the second invocation");
17691        assert!(
17692            swallowing_run.invocation_end < swallowing.end_byte(),
17693            "the second invocation swallowed the third"
17694        );
17695        assert_eq!(
17696            swallowing_run.region_end,
17697            root.end_byte(),
17698            "a swallowing invocation owns the region to the close of its declaration scope"
17699        );
17700
17701        let parsed = parse_cpp_declarations(source, "library.h");
17702        assert_eq!(
17703            function_identities(&parsed),
17704            vec![
17705                (
17706                    "library_init".to_string(),
17707                    "(struct library_model_loader *)".to_string()
17708                ),
17709                (
17710                    "library_init_from_buffer".to_string(),
17711                    "(void *, size_t)".to_string()
17712                ),
17713                (
17714                    "library_init_from_file".to_string(),
17715                    "(const char *)".to_string()
17716                ),
17717            ],
17718            "{:#?}",
17719            parsed.declarations()
17720        );
17721    }
17722
17723    /// The hand-off that keeps the scan finite. An invocation that reaches the
17724    /// end of its own declaration scope swallowed nothing, and the ordinary
17725    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
17726    /// that region for every invocation it finds, so admitting it here would
17727    /// hand the scan its own input back.
17728    #[test]
17729    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
17730        let source = r#"LIBRARY_DEPRECATED(
17731        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
17732        "use library_init_from_file_with_params instead"
17733    );"#;
17734        let mut parser = tree_sitter::Parser::new();
17735        parser
17736            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17737            .expect("C++ grammar");
17738        let tree = parser.parse(source, None).expect("C++ tree");
17739        let root = tree.root_node();
17740        let head = root.named_child(0).expect("the invocation");
17741        let ancestry = ParentIndex::new(root);
17742        assert!(
17743            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
17744            "{}",
17745            root.to_sexp()
17746        );
17747        assert!(
17748            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
17749            "the ordinary reader must be the one that has it: {}",
17750            root.to_sexp()
17751        );
17752        // The index is a speed substitution, never a semantic one: the
17753        // unindexed path is `Node::parent` itself and must agree.
17754        let unindexed = ParentIndex::unindexed();
17755        assert!(
17756            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
17757            "indexed and unindexed climbs must agree"
17758        );
17759        assert_eq!(
17760            macro_wrapped_declarations(head, source, &ancestry).len(),
17761            macro_wrapped_declarations(head, source, &unindexed).len(),
17762            "indexed and unindexed climbs must agree"
17763        );
17764    }
17765
17766    /// Negative controls for the same recovery: a macro invocation whose
17767    /// arguments are not a declaration recovers nothing, whether the parser
17768    /// keeps it clean, reads the arguments as a type, or reads them as a bare
17769    /// declarator.
17770    #[test]
17771    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
17772        for source in [
17773            "int before;\nFOO(1, 2);\nint after;\n",
17774            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
17775            "int before;\nMACRO(int a, int b);\nint after;\n",
17776            "DECLARE_HANDLE(HWND);\nint after;\n",
17777        ] {
17778            let parsed = parse_cpp_declarations(source, "macro-call.h");
17779            assert_eq!(
17780                function_identities(&parsed),
17781                Vec::new(),
17782                "{source:?} must declare no function: {:#?}",
17783                parsed.declarations()
17784            );
17785        }
17786    }
17787
17788    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
17789    /// makes tree-sitter read the macro as the member's type and its argument
17790    /// list as a parenthesized declarator, which then swallows the attributed
17791    /// member and the member written after it. Both are members.
17792    #[test]
17793    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
17794        let source = r#"#include <string_view>
17795namespace Botan {
17796class DL_Group final {
17797   public:
17798      DL_Group() = default;
17799      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17800      DL_Group(std::string_view pem, int format);
17801      size_t get_p() const;
17802};
17803}
17804"#;
17805        let parsed = parse_cpp_declarations(source, "dl_group.h");
17806        assert_eq!(
17807            function_identities(&parsed),
17808            vec![
17809                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
17810                (
17811                    "Botan.DL_Group.DL_Group".to_string(),
17812                    "(std::string_view)".to_string()
17813                ),
17814                (
17815                    "Botan.DL_Group.DL_Group".to_string(),
17816                    "(std::string_view, int)".to_string()
17817                ),
17818                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17819            ],
17820            "{:#?}",
17821            parsed.declarations()
17822        );
17823    }
17824
17825    /// #2552 shape 3, export-macro class. The class head macro makes the body a
17826    /// `compound_statement`, so members come from the region reparse, and one
17827    /// string-attribute member used to make that reparse unindexable -- which
17828    /// left the class with no members at all.
17829    #[test]
17830    fn an_export_macro_class_keeps_its_string_attribute_members() {
17831        let source = r#"#include <string_view>
17832namespace Botan {
17833class BOTAN_PUBLIC_API(2, 0) DL_Group final {
17834   public:
17835      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
17836      DL_Group(std::string_view pem, int format);
17837      size_t get_p() const;
17838};
17839}
17840"#;
17841        let parsed = parse_cpp_declarations(source, "dl_group.h");
17842        assert!(
17843            parsed
17844                .declarations()
17845                .iter()
17846                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
17847            "{:#?}",
17848            parsed.declarations()
17849        );
17850        assert_eq!(
17851            function_identities(&parsed),
17852            vec![
17853                (
17854                    "Botan.DL_Group.DL_Group".to_string(),
17855                    "(std::string_view)".to_string()
17856                ),
17857                (
17858                    "Botan.DL_Group.DL_Group".to_string(),
17859                    "(std::string_view, int)".to_string()
17860                ),
17861                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
17862            ],
17863            "{:#?}",
17864            parsed.declarations()
17865        );
17866    }
17867
17868    /// #2552 shape 3, the `= default` variant plus the access-label
17869    /// constructor. The attributed defaulted constructor separates cleanly, but
17870    /// it strands the next member in a bare `ERROR`, and a constructor written
17871    /// with a member-initializer list under `private:` dissolves into
17872    /// expression soup that the reparse recovers from its own source range.
17873    #[test]
17874    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
17875        let source = r#"namespace Botan {
17876class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
17877   public:
17878      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
17879      XMSS_Parameters(int oid, int len);
17880      size_t len() const;
17881
17882   private:
17883      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
17884            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
17885
17886      int m_oid;
17887      int m_wots_oid;
17888      size_t m_element_size;
17889      size_t m_tree_height;
17890};
17891}
17892"#;
17893        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
17894        let constructors = function_identities(&parsed)
17895            .into_iter()
17896            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
17897            .map(|(_, signature)| signature)
17898            .collect::<Vec<_>>();
17899        assert_eq!(
17900            constructors,
17901            vec![
17902                "()".to_string(),
17903                "(int, int)".to_string(),
17904                "(int, int, size_t, size_t)".to_string(),
17905            ],
17906            "{:#?}",
17907            parsed.declarations()
17908        );
17909    }
17910
17911    /// Negative control for the same rule: a real qualified name spells its
17912    /// `::` in the source, so the separator is present rather than MISSING and
17913    /// the out-of-line definition keeps its owner.
17914    #[test]
17915    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
17916        let source = r#"namespace shell {
17917struct Outer {
17918   struct Inner {
17919      Inner(int v);
17920      void run(int v);
17921   };
17922};
17923Outer::Inner::Inner(int v) {}
17924void Outer::Inner::run(int v) {}
17925}
17926"#;
17927        let parsed = parse_cpp_declarations(source, "outer.cpp");
17928        let names = function_identities(&parsed)
17929            .into_iter()
17930            .map(|(fq_name, _)| fq_name)
17931            .collect::<Vec<_>>();
17932        assert!(
17933            names
17934                .iter()
17935                .all(|name| name.starts_with("shell.Outer$Inner.")),
17936            "{names:#?}"
17937        );
17938    }
17939
17940    #[test]
17941    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
17942        let source = r#"namespace control {
17943template <typename T>
17944class AnySpan;
17945template <typename T>
17946class ABSL_ATTRIBUTE_VIEW AnySpan {
17947 public:
17948  int begin() const;
17949};
17950}
17951
17952namespace absl {
17953ABSL_NAMESPACE_BEGIN
17954template <typename T>
17955class ABSL_ATTRIBUTE_VIEW Span {
17956 public:
17957  int begin() const;
17958  int back() const;
17959};
17960
17961int begin();
17962int back();
17963}
17964"#;
17965        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
17966        let declarations = parsed.declarations();
17967        assert!(
17968            declarations
17969                .iter()
17970                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
17971        );
17972        for method in ["begin", "back"] {
17973            assert!(declarations.iter().any(|unit| {
17974                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
17975            }));
17976            assert!(
17977                declarations.iter().any(|unit| {
17978                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
17979                })
17980            );
17981        }
17982        assert!(
17983            declarations
17984                .iter()
17985                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
17986        );
17987        assert!(
17988            declarations
17989                .iter()
17990                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
17991        );
17992        assert!(
17993            declarations
17994                .iter()
17995                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
17996        );
17997    }
17998
17999    #[test]
18000    fn explicit_global_member_definition_has_canonical_package_boundary() {
18001        let source = r#"
18002namespace arangodb::aql {
18003class ExecutionPlan {
18004 public:
18005  template<class... Args> Node* createNode(Args&&... args);
18006};
18007}
18008
18009template<class... Args>
18010Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18011"#;
18012        let parsed = parse_cpp_declarations(source, "global-member.cpp");
18013
18014        assert!(parsed.declarations().iter().any(|unit| {
18015            unit.is_function()
18016                && unit.package_name() == "arangodb::aql"
18017                && unit.short_name() == "ExecutionPlan.createNode"
18018                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18019        }));
18020    }
18021
18022    #[test]
18023    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18024        let source = r#"
18025#ifndef TINYXML2_INCLUDED
18026#define TINYXML2_INCLUDED
18027namespace tinyxml2 {
18028class TINYXML2_LIB XMLUtil {
18029 public:
18030  static const char* SkipWhiteSpace(const char* p) {
18031    while (*p) {
18032      if (*p == ' ') {
18033        ++p;
18034      }
18035    }
18036    return p;
18037  }
18038  static bool StringEqual(const char* p, const char* q) {
18039    return p == q;
18040  }
18041  class TINYXML2_LIB Helper {
18042   public:
18043    void Touch();
18044  };
18045  static void ToStr(int value, char* buffer);
18046 private:
18047  static const char* writeBoolTrue;
18048};
18049
18050class TINYXML2_LIB XMLNode {
18051 public:
18052  virtual XMLNode* ShallowClone() const = 0;
18053  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18054};
18055}
18056#endif
18057"#;
18058        let mut parser = tree_sitter::Parser::new();
18059        parser
18060            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18061            .unwrap();
18062        let tree = parser.parse(source, None).unwrap();
18063        let mut boundary_found = false;
18064        walk_named_tree_preorder(tree.root_node(), true, |node| {
18065            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18066                && name == "XMLUtil"
18067            {
18068                boundary_found = fragmented_export_sibling_class_boundary(node, source)
18069                    .and_then(|boundary| {
18070                        recover_exported_class_function_definition(boundary, source)
18071                    })
18072                    .is_some_and(|(_, name, _)| name == "XMLNode");
18073            }
18074            WalkControl::Continue
18075        });
18076        assert!(
18077            boundary_found,
18078            "fixture must exercise the recovered sibling boundary"
18079        );
18080
18081        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18082        assert!(
18083            parsed
18084                .declarations()
18085                .iter()
18086                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18087            "{:#?}",
18088            parsed.declarations()
18089        );
18090        assert!(
18091            parsed
18092                .declarations()
18093                .iter()
18094                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18095            "{:#?}",
18096            parsed.declarations()
18097        );
18098        assert!(parsed.declarations().iter().any(|unit| {
18099            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18100        }));
18101        assert!(
18102            parsed
18103                .declarations()
18104                .iter()
18105                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18106        );
18107        assert!(
18108            parsed
18109                .declarations()
18110                .iter()
18111                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18112        );
18113    }
18114
18115    #[test]
18116    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18117        // Clang's diagnostic suite intentionally contains this ill-formed
18118        // spelling. The analyzer must retain the parser's explicit-global AST
18119        // boundary instead of constructing `cwg311::::cwg311::X`.
18120        let parsed = parse_cpp_declarations(
18121            r#"
18122namespace cwg311 {
18123namespace X { namespace Y {} }
18124namespace ::cwg311::X {}
18125}
18126"#,
18127            "explicit-global-namespace.cpp",
18128        );
18129
18130        assert!(parsed.declarations().iter().any(|unit| {
18131            unit.kind() == CodeUnitType::Module
18132                && unit.short_name() == "cwg311::X"
18133                && unit.fq_name() == "cwg311::X"
18134        }));
18135        assert!(
18136            parsed
18137                .declarations()
18138                .iter()
18139                .all(|unit| !unit.short_name().contains("::::")),
18140            "recovered namespace names must not retain empty scope components: {:#?}",
18141            parsed.declarations()
18142        );
18143    }
18144
18145    #[test]
18146    fn repeated_scope_separator_does_not_create_empty_function_owner() {
18147        let scope = ScopeInfo {
18148            package_name: "X".to_string(),
18149            module: None,
18150            class_unit: None,
18151            template_signature: None,
18152            template_metadata: None,
18153            declarations_are_fields: false,
18154            recovered_specialization_member_scope: false,
18155            visible_using_namespaces: Vec::new(),
18156        };
18157
18158        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18159
18160        assert!(owner.is_none());
18161        assert_eq!(name, "doit");
18162        assert_eq!(package, "X");
18163    }
18164
18165    #[test]
18166    fn trailing_decltype_expression_is_not_a_function_declarator() {
18167        let source = r#"
18168namespace boost { namespace detail {
18169#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18170    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18171    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18172#define BOOST_THREAD_PROVIDES_INVOKE
18173#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18174template <class Fp, class A0, class ...Args>
18175inline auto
18176invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18177       BOOST_THREAD_RV_REF(Args) ...args)
18178    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18179{
18180    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18181}
18182#endif
18183#endif
18184}}
18185"#;
18186        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18187
18188        assert!(
18189            parsed
18190                .declarations()
18191                .iter()
18192                .all(|unit| unit.short_name() != ".*f")
18193        );
18194    }
18195
18196    fn find_class_named<'tree>(
18197        root: Node<'tree>,
18198        source: &str,
18199        expected_name: &str,
18200    ) -> Option<Node<'tree>> {
18201        let mut stack = vec![root];
18202        while let Some(node) = stack.pop() {
18203            if node.kind() == "class_specifier"
18204                && node
18205                    .child_by_field_name("name")
18206                    .is_some_and(|name| node_text(name, source) == expected_name)
18207            {
18208                return Some(node);
18209            }
18210            let mut cursor = node.walk();
18211            stack.extend(node.named_children(&mut cursor));
18212        }
18213        None
18214    }
18215
18216    #[test]
18217    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18218        let source = r#"EXPORT void definition(struct Value value) {}
18219EXPORT void prototype(struct Value value);
18220"#;
18221        let mut parser = tree_sitter::Parser::new();
18222        parser
18223            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18224            .unwrap();
18225        let tree = parser.parse(source, None).unwrap();
18226        let root = tree.root_node();
18227        let mut cursor = root.walk();
18228        let callables = root
18229            .named_children(&mut cursor)
18230            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18231            .collect::<Vec<_>>();
18232
18233        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18234        for callable in callables {
18235            assert!(callable.has_error(), "fixture must exercise error recovery");
18236            assert!(
18237                cpp_sentinel_macro_parts(callable, source).is_none(),
18238                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18239            );
18240        }
18241    }
18242
18243    #[test]
18244    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18245        let source = r#"namespace absl {
18246ABSL_NAMESPACE_BEGIN
18247// Generate a floating-point variate conforming to a Beta distribution:
18248template <typename RealType = double>
18249class beta_distribution {
18250 public:
18251  using result_type = RealType;
18252
18253
18254  beta_distribution() : beta_distribution(1) {}
18255
18256  explicit beta_distribution(result_type alpha, result_type beta = 1)
18257      : param_(alpha, beta) {}
18258
18259  explicit beta_distribution(const param_type& p) : param_(p) {}
18260
18261  void reset() {}
18262
18263  // Generating functions
18264  template <typename URBG>
18265  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
18266    return (*this)(g, param_);
18267  }
18268
18269};
18270ABSL_NAMESPACE_END
18271}  // namespace absl
18272"#;
18273        let mut parser = tree_sitter::Parser::new();
18274        parser
18275            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18276            .unwrap();
18277        let tree = parser.parse(source, None).unwrap();
18278        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18279        let body = namespace
18280            .child_by_field_name("body")
18281            .expect("fixture namespace body");
18282        let sentinel = body.named_child(0).expect("sentinel envelope");
18283        let callable = sentinel
18284            .child_by_field_name("declarator")
18285            .and_then(extract_function_declarator)
18286            .and_then(cpp_function_declarator_name_node)
18287            .expect("preserved callable name");
18288
18289        assert_eq!(sentinel.kind(), "function_definition");
18290        assert_eq!(callable.kind(), "operator_name");
18291        assert!(
18292            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18293            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18294        );
18295    }
18296
18297    #[test]
18298    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18299        let source = r#"namespace absl {
18300ABSL_NAMESPACE_BEGIN
18301// absl::discrete_distribution
18302//
18303// A discrete distribution produces random integers i, where 0 <= i < n
18304template <typename IntType = int>
18305class discrete_distribution {
18306 public:
18307  using result_type = IntType;
18308  class param_type {
18309   public:
18310    param_type() { init(); }
18311    template <typename InputIterator>
18312    explicit param_type(InputIterator begin, InputIterator end)
18313        : p_(begin, end) {
18314      init();
18315    }
18316  };
18317  discrete_distribution() : param_() {}
18318  explicit discrete_distribution(const param_type& p) : param_(p) {}
18319};
18320ABSL_NAMESPACE_END
18321}  // namespace absl
18322"#;
18323        let mut parser = tree_sitter::Parser::new();
18324        parser
18325            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18326            .unwrap();
18327        let tree = parser.parse(source, None).unwrap();
18328        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18329        let body = namespace
18330            .child_by_field_name("body")
18331            .expect("fixture namespace body");
18332        let sentinel = body.named_child(0).expect("sentinel envelope");
18333        let callable = sentinel
18334            .child_by_field_name("declarator")
18335            .and_then(extract_function_declarator)
18336            .and_then(cpp_function_declarator_name_node)
18337            .expect("preserved callable name");
18338
18339        assert_eq!(sentinel.kind(), "function_definition");
18340        assert_eq!(callable.kind(), "identifier");
18341        assert!(
18342            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18343            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
18344        );
18345    }
18346
18347    #[test]
18348    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
18349        let source = r#"
18350#define CPPCHECKLIB
18351class Library {
18352    struct Container {
18353        CPPCHECKLIB static std::string toString(Yield yield);
18354        CPPCHECKLIB static std::string toString(Action action);
18355    };
18356};
18357"#;
18358        let mut parser = tree_sitter::Parser::new();
18359        parser
18360            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18361            .unwrap();
18362        let tree = parser.parse(source, None).unwrap();
18363        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
18364        let parsed = parse_cpp_file(&file, source, &tree);
18365        assert!(
18366            parsed
18367                .declarations()
18368                .iter()
18369                .all(|unit| unit.fq_name() != "Library$Container.std"),
18370            "the qualified return-type namespace must not become a field: {:#?}",
18371            parsed.declarations()
18372        );
18373        for expected in ["(Yield)", "(Action)"] {
18374            assert!(
18375                parsed.declarations().iter().any(|unit| {
18376                    unit.is_function()
18377                        && unit.fq_name() == "Library$Container.toString"
18378                        && unit.signature() == Some(expected)
18379                }),
18380                "recovered toString overload {expected} is missing: {:#?}",
18381                parsed.declarations()
18382            );
18383        }
18384    }
18385
18386    #[test]
18387    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
18388        let source = r#"
18389#define SIMPLECPP_LIB
18390namespace simplecpp {
18391using TokenString = std::string;
18392struct Location { int line{}; };
18393class SIMPLECPP_LIB Token {
18394  TokenString prefix;
18395  void prefix_method() {}
18396 public:
18397  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18398      whitespaceahead(wsahead), location(loc), string(s)
18399      // The comment must not hide the constructor body from recovery.
18400      {
18401      flags();
18402  }
18403  TokenString string;
18404  bool whitespaceahead;
18405  Location location;
18406  Token *previous{};
18407 private:
18408  void flags() {
18409      whitespaceahead = true;
18410  }
18411};
18412}
18413"#;
18414        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
18415
18416        let location_fields = parsed
18417            .declarations()
18418            .iter()
18419            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
18420            .collect::<Vec<_>>();
18421        assert_eq!(
18422            location_fields.len(),
18423            1,
18424            "location should have one class-owned declaration: {:#?}",
18425            parsed.declarations()
18426        );
18427        assert!(
18428            location_fields[0].is_field(),
18429            "location has wrong kind: {:#?}",
18430            parsed.declarations()
18431        );
18432        assert!(
18433            parsed.declarations().iter().all(|unit| {
18434                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
18435            })
18436        );
18437        assert!(
18438            parsed.declarations().iter().all(|unit| {
18439                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18440            })
18441        );
18442        assert!(
18443            parsed
18444                .declarations()
18445                .iter()
18446                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
18447        );
18448        assert!(
18449            parsed
18450                .declarations()
18451                .iter()
18452                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
18453            "the recovered class must retain its constructor: {:#?}",
18454            parsed.declarations()
18455        );
18456        assert!(
18457            parsed
18458                .declarations()
18459                .iter()
18460                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
18461        );
18462        assert!(parsed.declarations().iter().any(|unit| {
18463            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
18464        }));
18465        let constructor = parsed
18466            .declarations()
18467            .iter()
18468            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
18469            .expect("recovered constructor");
18470        let constructor_start = source.find("Token(const").expect("constructor start");
18471        let constructor_end = source
18472            .get(
18473                ..source
18474                    .find("  TokenString string;")
18475                    .expect("constructor end"),
18476            )
18477            .expect("constructor slice")
18478            .trim_end()
18479            .len();
18480        assert!(
18481            parsed
18482                .navigation_ranges
18483                .get(constructor)
18484                .is_some_and(|ranges| {
18485                    ranges.iter().any(|range| {
18486                        range.start_byte == constructor_start && range.end_byte == constructor_end
18487                    })
18488                }),
18489            "constructor navigation must span the full body: {:#?}",
18490            parsed.navigation_ranges
18491        );
18492        assert_eq!(
18493            parsed
18494                .signature_metadata
18495                .get(constructor)
18496                .and_then(|metadata| metadata.first())
18497                .and_then(SignatureMetadata::callable_linkage),
18498            Some(CallableLinkage::External)
18499        );
18500        let token_class = parsed
18501            .declarations()
18502            .iter()
18503            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
18504            .expect("recovered Token class");
18505        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
18506        assert!(
18507            parsed
18508                .navigation_ranges
18509                .get(token_class)
18510                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
18511            "class navigation must include the terminating semicolon: {:#?}",
18512            parsed.navigation_ranges
18513        );
18514    }
18515
18516    #[test]
18517    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
18518        let source = r#"
18519#define SIMPLECPP_LIB
18520namespace simplecpp {
18521using TokenString = std::string;
18522class Macro;
18523struct Location {
18524  unsigned int fileIndex{};
18525  unsigned int line{};
18526  unsigned int col{};
18527};
18528struct Output {
18529  int type;
18530};
18531class SIMPLECPP_LIB Token {
18532 public:
18533  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
18534      whitespaceahead(wsahead), location(loc), string(s) {
18535      flags();
18536  }
18537  Token(const Token &tok) :
18538      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
18539      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
18540      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
18541  Token &operator=(const Token &tok) = delete;
18542  const TokenString& str() const { return string; }
18543  void setstr(const std::string &s) { string = s; flags(); }
18544  bool isOneOf(const char ops[]) const;
18545  TokenString macro;
18546  char op;
18547  bool comment;
18548  bool name;
18549  bool number;
18550  bool whitespaceahead;
18551  Location location;
18552  Token *previous{};
18553  Token *next{};
18554 private:
18555  void flags() {
18556      name = !string.empty();
18557      comment = false;
18558      number = false;
18559      op = 0;
18560  }
18561  TokenString string;
18562};
18563}
18564struct Following {
18565  int type;
18566};
18567class SIMPLECPP_LIB Later {
18568 public:
18569  Later(int value) : value(value) {}
18570  int value;
18571};
18572"#;
18573        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
18574        assert!(
18575            parsed
18576                .declarations()
18577                .iter()
18578                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
18579        );
18580        assert!(
18581            !parsed
18582                .declarations()
18583                .iter()
18584                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
18585        );
18586        assert!(
18587            parsed
18588                .declarations()
18589                .iter()
18590                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
18591        );
18592        assert!(
18593            !parsed
18594                .declarations()
18595                .iter()
18596                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
18597        );
18598        assert!(
18599            parsed
18600                .declarations()
18601                .iter()
18602                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
18603        );
18604        assert!(
18605            parsed
18606                .declarations()
18607                .iter()
18608                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
18609        );
18610        assert!(
18611            parsed
18612                .declarations()
18613                .iter()
18614                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
18615        );
18616        assert!(
18617            parsed
18618                .declarations()
18619                .iter()
18620                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
18621        );
18622        assert!(
18623            parsed
18624                .declarations()
18625                .iter()
18626                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
18627        );
18628        assert!(
18629            parsed
18630                .declarations()
18631                .iter()
18632                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
18633        );
18634        assert!(parsed.declarations().iter().all(|unit| {
18635            !matches!(
18636                unit.fq_name().as_str(),
18637                "simplecpp.Token.Following" | "simplecpp.Token.Later"
18638            )
18639        }));
18640        assert!(
18641            !parsed
18642                .declarations()
18643                .iter()
18644                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
18645            "the following struct must remain outside the recovered Token class"
18646        );
18647    }
18648
18649    #[test]
18650    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
18651        let source = r#"
18652#define SIMPLECPP_LIB
18653namespace {
18654namespace simplecpp {
18655using TokenString = std::string;
18656struct Location { int line{}; };
18657class SIMPLECPP_LIB HiddenToken {
18658 public:
18659  HiddenToken(const TokenString &s, const Location &loc) :
18660      location(loc), string(s) {
18661      flags();
18662  }
18663  TokenString string;
18664  Location location;
18665  HiddenToken *previous{};
18666 private:
18667  void flags() {}
18668};
18669}
18670}
18671"#;
18672        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
18673        let constructor = parsed
18674            .declarations()
18675            .iter()
18676            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
18677            .expect("recovered anonymous-namespace constructor");
18678        assert_eq!(
18679            parsed
18680                .signature_metadata
18681                .get(constructor)
18682                .and_then(|metadata| metadata.first())
18683                .and_then(SignatureMetadata::callable_linkage),
18684            Some(CallableLinkage::Internal)
18685        );
18686    }
18687
18688    #[test]
18689    fn macro_qualified_static_field_keeps_real_declarator() {
18690        let source = r#"#define JSON_INLINE_VARIABLE
18691struct Reader {
18692static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
18693static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
18694static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
18695};"#;
18696        let mut parser = tree_sitter::Parser::new();
18697        parser
18698            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18699            .unwrap();
18700        let tree = parser.parse(source, None).unwrap();
18701        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
18702        let parsed = parse_cpp_file(&file, source, &tree);
18703        for expected in [
18704            "Reader.npos",
18705            "Reader.other",
18706            "Reader.pointer",
18707            "Reader.reference",
18708        ] {
18709            assert!(
18710                parsed
18711                    .declarations()
18712                    .iter()
18713                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
18714                "real macro-decorated field {expected} is missing: {:#?}",
18715                parsed.declarations()
18716            );
18717        }
18718        assert!(
18719            parsed
18720                .declarations()
18721                .iter()
18722                .all(|unit| unit.fq_name() != "Reader.std"),
18723            "qualified type prefix became a pseudo-field: {:#?}",
18724            parsed.declarations()
18725        );
18726        let root = tree.root_node();
18727        let mut stack = vec![root];
18728        let mut signatures = Vec::new();
18729        while let Some(current) = stack.pop() {
18730            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
18731            {
18732                signatures.extend(
18733                    declarators
18734                        .into_iter()
18735                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
18736                );
18737            }
18738            let mut cursor = current.walk();
18739            stack.extend(current.named_children(&mut cursor));
18740        }
18741        signatures.sort();
18742        assert_eq!(
18743            signatures,
18744            [
18745                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
18746                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
18747                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
18748                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
18749            ]
18750        );
18751    }
18752
18753    fn member_function_linkage(source: &str) -> CallableLinkage {
18754        let mut parser = tree_sitter::Parser::new();
18755        parser
18756            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18757            .unwrap();
18758        let tree = parser.parse(source, None).unwrap();
18759        let ancestry = ParentIndex::new(tree.root_node());
18760        let mut stack = vec![tree.root_node()];
18761        while let Some(node) = stack.pop() {
18762            if node.kind() == "function_definition" {
18763                let mut current = node.parent();
18764                while let Some(parent) = current {
18765                    if matches!(
18766                        parent.kind(),
18767                        "class_specifier" | "struct_specifier" | "union_specifier"
18768                    ) {
18769                        return cpp_callable_linkage(node, source, &ancestry);
18770                    }
18771                    current = parent.parent();
18772                }
18773            }
18774            let mut cursor = node.walk();
18775            stack.extend(node.named_children(&mut cursor));
18776        }
18777        panic!("fixture has no member function definition");
18778    }
18779
18780    #[test]
18781    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
18782        assert_eq!(
18783            member_function_linkage("struct Named { int method() { return 1; } };"),
18784            CallableLinkage::External
18785        );
18786        assert_eq!(
18787            member_function_linkage(
18788                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
18789            ),
18790            CallableLinkage::Internal
18791        );
18792        assert_eq!(
18793            member_function_linkage("struct { int method() { return 1; } } instance;"),
18794            CallableLinkage::Internal
18795        );
18796        assert_eq!(
18797            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
18798            CallableLinkage::Internal
18799        );
18800    }
18801
18802    #[test]
18803    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
18804        let source = r#"
18805#ifndef PROTON_VALUE_HPP
18806#define PROTON_VALUE_HPP
18807namespace proton {
18808namespace internal {
18809class value_base {
18810  protected:
18811    internal::data& data();
18812    internal::data data_;
18813  friend class codec::encoder;
18814  friend class codec::decoder;
18815};
18816}
18817class value : public internal::value_base, private internal::comparable<value> {
18818  private:
18819    template<class T, class U=void> struct assignable :
18820        public std::enable_if<codec::is_encodable<T>::value, U> {};
18821    template<class U> struct assignable<value, U> {};
18822  public:
18823    PN_CPP_EXTERN value();
18824    PN_CPP_EXTERN value(const value&);
18825    PN_CPP_EXTERN value& operator=(const value&);
18826    PN_CPP_EXTERN value(value&&);
18827    PN_CPP_EXTERN value& operator=(value&&);
18828    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
18829    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
18830        codec::encoder e(*this);
18831        e << x;
18832        return *this;
18833    }
18834    PN_CPP_EXTERN type_id type() const;
18835    PN_CPP_EXTERN bool empty() const;
18836    PN_CPP_EXTERN void clear();
18837    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
18838    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
18839  friend PN_CPP_EXTERN void swap(value&, value&);
18840  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
18841  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
18842  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
18843    value(pn_data_t* d);
18844    void reset(pn_data_t* d = 0);
18845};
18846}
18847#endif
18848"#;
18849        let mut parser = tree_sitter::Parser::new();
18850        parser
18851            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18852            .unwrap();
18853        let tree = parser.parse(source, None).unwrap();
18854        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
18855        let parsed = parse_cpp_file(&file, source, &tree);
18856        let macro_constructors = parsed
18857            .signature_metadata
18858            .iter()
18859            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
18860            .flat_map(|(_, metadata)| metadata)
18861            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
18862            .collect::<Vec<_>>();
18863
18864        assert_eq!(
18865            macro_constructors.len(),
18866            3,
18867            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
18868            parsed.declarations()
18869        );
18870        assert!(
18871            macro_constructors.iter().all(|metadata| {
18872                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
18873            }),
18874            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
18875        );
18876    }
18877
18878    #[test]
18879    fn recovered_export_class_typedef_uses_displaced_alias_name() {
18880        let source = r#"
18881namespace spi {
18882class Filter {
18883public:
18884    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
18885};
18886}
18887namespace filter {
18888class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
18889{
18890public:
18891    typedef spi::Filter BASE_CLASS;
18892    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
18893    BEGIN_LOG4CXX_CAST_MAP()
18894    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
18895    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
18896    END_LOG4CXX_CAST_MAP()
18897    FilterDecision decide() const;
18898};
18899}
18900"#;
18901        let mut parser = tree_sitter::Parser::new();
18902        parser
18903            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18904            .unwrap();
18905        let tree = parser.parse(source, None).unwrap();
18906        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
18907        let parsed = parse_cpp_file(&file, source, &tree);
18908        assert!(
18909            parsed.declarations().iter().any(|unit| {
18910                unit.is_class()
18911                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
18912                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
18913            }),
18914            "the displaced typedef alias must retain its declared name: {:#?}",
18915            parsed.declarations()
18916        );
18917        assert!(
18918            parsed
18919                .declarations()
18920                .iter()
18921                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
18922            "the qualified underlying type must not become a false nested alias: {:#?}",
18923            parsed.declarations()
18924        );
18925    }
18926
18927    #[test]
18928    fn exported_single_base_recovery_uses_displaced_class_name() {
18929        let source = r#"
18930class CORE_EXPORT QgsPoint : public AbstractGeometry
18931{
18932    Q_GADGET
18933
18934    Q_PROPERTY( double x READ x WRITE setX )
18935    Q_PROPERTY( double y READ y WRITE setY )
18936    Q_PROPERTY( double z READ z WRITE setZ )
18937    Q_PROPERTY( double m READ m WRITE setM )
18938
18939  public:
18940#ifndef SIP_RUN
18941    QgsPoint(
18942      double x = std::numeric_limits<double>::quiet_NaN(),
18943      double y = std::numeric_limits<double>::quiet_NaN(),
18944      double z = std::numeric_limits<double>::quiet_NaN(),
18945      double m = std::numeric_limits<double>::quiet_NaN(),
18946      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
18947    );
18948#else
18949    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 )];
18950    % MethodCode
18951    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18952    {
18953      int state;
18954      sipIsErr = 0;
18955      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18956      if ( !sipIsErr )
18957      {
18958        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18959      }
18960      sipReleaseType( p, sipType_QgsPointXY, state );
18961    }
18962    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
18963    {
18964      int state;
18965      sipIsErr = 0;
18966
18967      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
18968      if ( !sipIsErr )
18969      {
18970        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
18971      }
18972      sipReleaseType( p, sipType_QPointF, state );
18973    }
18974    else if (
18975      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
18976      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
18977      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
18978      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
18979    {
18980      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
18981      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
18982      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
18983      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
18984      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
18985      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
18986    }
18987    else // Invalid ctor arguments
18988    {
18989      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
18990      sipIsErr = 1;
18991    }
18992    % End
18993#endif
18994
18995    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
18996    explicit QgsPoint( QPointF p ) SIP_SKIP;
18997    explicit QgsPoint(
18998      Qgis::WkbType wkbType,
18999      double x = std::numeric_limits<double>::quiet_NaN(),
19000      double y = std::numeric_limits<double>::quiet_NaN(),
19001      double z = std::numeric_limits<double>::quiet_NaN(),
19002      double m = std::numeric_limits<double>::quiet_NaN()
19003    ) SIP_SKIP;
19004    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19005    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19006    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19007#ifndef SIP_RUN
19008  private:
19009    bool fuzzyHelper(
19010      double epsilon,
19011      const AbstractGeometry &other,
19012      bool is3DFlag,
19013      bool isMeasureFlag
19014    ) const
19015    {
19016      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19017    }
19018#endif
19019};
19020class Ordinary : public Base { public: Ordinary(); };
19021class API_EXPORT Plain { public: Plain(); };
19022class API_EXPORT : public Base {};
19023class
19024PN_CPP_CLASS_EXTERN Sender : public Link {
19025    Sender();
19026    struct impl;
19027    struct impl& get_impl() const;
19028};
19029class thread_ctx_t {};
19030class ctx_t ZMQ_FINAL : public thread_ctx_t {
19031    bool start();
19032};
19033"#;
19034        let mut parser = tree_sitter::Parser::new();
19035        parser
19036            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19037            .unwrap();
19038        let tree = parser.parse(source, None).unwrap();
19039        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19040        let parsed = parse_cpp_file(&file, source, &tree);
19041        let declarations = parsed.declarations();
19042
19043        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19044            assert!(
19045                declarations
19046                    .iter()
19047                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
19048                "missing recovered class {expected}: {declarations:#?}"
19049            );
19050        }
19051        let qgs_point = declarations
19052            .iter()
19053            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19054            .expect("recovered QgsPoint class");
19055        assert_eq!(
19056            parsed.raw_supertypes.get(qgs_point),
19057            Some(&vec!["AbstractGeometry".to_string()]),
19058            "single-base export recovery must retain its displaced base"
19059        );
19060        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19061        assert!(
19062            parsed
19063                .navigation_ranges
19064                .get(qgs_point)
19065                .is_some_and(|ranges| {
19066                    !ranges.is_empty()
19067                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19068                }),
19069            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19070            parsed.navigation_ranges.get(qgs_point)
19071        );
19072        let sender = declarations
19073            .iter()
19074            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19075            .expect("recovered Sender class");
19076        assert_eq!(
19077            parsed.raw_supertypes.get(sender),
19078            Some(&vec!["Link".to_string()]),
19079            "post-declarator export recovery must retain its displaced base"
19080        );
19081        let recovered_member = declarations
19082            .iter()
19083            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19084            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19085        assert_eq!(
19086            parsed
19087                .signature_metadata
19088                .get(recovered_member)
19089                .and_then(|metadata| metadata.first())
19090                .and_then(SignatureMetadata::callable_linkage),
19091            Some(CallableLinkage::External),
19092            "a named recovered class's members have external linkage"
19093        );
19094        let ctx = declarations
19095            .iter()
19096            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19097            .expect("recovered ctx_t class");
19098        assert_eq!(
19099            parsed.raw_supertypes.get(ctx),
19100            Some(&vec!["thread_ctx_t".to_string()]),
19101            "postfix export-macro recovery must retain its displaced base"
19102        );
19103        assert!(
19104            declarations.iter().any(|unit| {
19105                unit.is_function()
19106                    && unit.fq_name() == "QgsPoint.QgsPoint"
19107                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19108            }),
19109            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19110        );
19111        assert!(
19112            declarations.iter().all(|unit| {
19113                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19114            }),
19115            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19116        );
19117    }
19118
19119    #[test]
19120    fn function_like_export_macro_classes_keep_names_and_base_edges() {
19121        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
19122        // error: a plain body, a single base, `final` without a base, `final`
19123        // with one base, and `final` with a comma-separated base list.
19124        let source = r#"
19125namespace api {
19126class PROJECT_PUBLIC_API(2, 0) Prelude {
19127  public:
19128    Prelude();
19129};
19130class PROJECT_PUBLIC_API(2, 0) Base {
19131  public:
19132    Base(int value);
19133};
19134class PROJECT_PUBLIC_API(2, 0) Mixin {
19135  public:
19136    Mixin();
19137};
19138class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19139  public:
19140    Adopted(int value);
19141};
19142class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19143  public:
19144    Derived(int value);
19145};
19146class PROJECT_PUBLIC_API(2, 0) Solo final {
19147  public:
19148    Solo();
19149};
19150class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19151  public:
19152    Blended(int value);
19153};
19154class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19155  public:
19156    Woven(int value);
19157};
19158} // namespace api
19159"#;
19160        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19161        let declarations = parsed.declarations();
19162        let class_named = |name: &str| {
19163            declarations
19164                .iter()
19165                .find(|unit| unit.is_class() && unit.fq_name() == name)
19166                .unwrap_or_else(|| {
19167                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19168                })
19169        };
19170        let base = class_named("api.Base");
19171        class_named("api.Prelude");
19172        class_named("api.Mixin");
19173
19174        assert_eq!(
19175            parsed.raw_supertypes.get(class_named("api.Adopted")),
19176            Some(&vec!["Base".to_string()])
19177        );
19178        assert_eq!(
19179            parsed.raw_supertypes.get(class_named("api.Derived")),
19180            Some(&vec!["Base".to_string()])
19181        );
19182        assert_eq!(
19183            parsed.raw_supertypes.get(class_named("api.Solo")),
19184            None,
19185            "a final class without a base list must not invent a supertype"
19186        );
19187        assert_eq!(
19188            parsed.raw_supertypes.get(class_named("api.Blended")),
19189            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19190        );
19191        assert_eq!(
19192            parsed.raw_supertypes.get(class_named("api.Woven")),
19193            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19194        );
19195        assert!(
19196            declarations
19197                .iter()
19198                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19199            "the export macro must not become a declaration: {declarations:#?}"
19200        );
19201        assert!(
19202            declarations.iter().all(|unit| !matches!(
19203                unit.identifier(),
19204                "final" | "public" | "protected" | "private"
19205            )),
19206            "the head specifiers must not become declarations: {declarations:#?}"
19207        );
19208        assert!(
19209            parsed
19210                .navigation_ranges
19211                .get(base)
19212                .is_some_and(|ranges| !ranges.is_empty()),
19213            "the recovered base must retain a navigable declaration range"
19214        );
19215    }
19216
19217    #[test]
19218    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19219        // #2557: the class name is the last identifier before the head ends
19220        // (`final`, the base clause `:`, or the body), whatever its spelling.
19221        // A class named in capitals (`X509_CA`) is a class, and an object-like
19222        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
19223        // the control whose spelling never mattered.
19224        let source = r#"
19225namespace api {
19226class PROJECT_PUBLIC_API(2, 0) Base {
19227  public:
19228    Base();
19229};
19230class PROJECT_PUBLIC_API(2, 0) Mixin {
19231  public:
19232    Mixin();
19233};
19234class PROJECT_PUBLIC_API(2, 0) Name {
19235  public:
19236    Name();
19237};
19238class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19239  public:
19240    X509_CA();
19241};
19242class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19243  public:
19244    HSS_LMS_KEY();
19245};
19246class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19247  public:
19248    GOST_3410();
19249};
19250class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19251  public:
19252    PKCS11_RSA();
19253};
19254class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19255  public:
19256    Plain();
19257};
19258class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19259  public:
19260    Decorated();
19261};
19262class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19263  public:
19264    Layered();
19265};
19266} // namespace api
19267"#;
19268        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19269        let declarations = parsed.declarations();
19270        let class_named = |name: &str| {
19271            declarations
19272                .iter()
19273                .find(|unit| unit.is_class() && unit.fq_name() == name)
19274                .unwrap_or_else(|| {
19275                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19276                })
19277        };
19278        for (name, bases) in [
19279            ("api.Name", None),
19280            ("api.X509_CA", None),
19281            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19282            ("api.GOST_3410", Some(vec!["Base"])),
19283            ("api.PKCS11_RSA", None),
19284            ("api.Plain", None),
19285            ("api.Decorated", Some(vec!["Base"])),
19286            ("api.Layered", Some(vec!["Base", "Mixin"])),
19287        ] {
19288            let expected =
19289                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19290            assert_eq!(
19291                parsed.raw_supertypes.get(class_named(name)),
19292                expected.as_ref(),
19293                "{name}"
19294            );
19295        }
19296        assert!(
19297            declarations.iter().all(|unit| !matches!(
19298                unit.identifier(),
19299                "PROJECT_PUBLIC_API"
19300                    | "OTHER_MACRO"
19301                    | "FIRST_MACRO"
19302                    | "SECOND_MACRO"
19303                    | "final"
19304                    | "public"
19305            )),
19306            "macros and head specifiers must not become declarations: {declarations:#?}"
19307        );
19308    }
19309
19310    #[test]
19311    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19312        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
19313        // head, the body's `{` and its first member in one declaration-scope
19314        // ERROR, scatters the remaining members across the container's
19315        // siblings, and ends the container on the class's own `}`. The class
19316        // was lost and the macro minted a class of its own.
19317        let source = r#"
19318namespace api {
19319
19320/**
19321* Doc comment
19322*/
19323class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19324   public:
19325      /**
19326      * Construct from a point.
19327      */
19328      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19329
19330#if defined(PROJECT_HAS_LEGACY_POINT)
19331      /**
19332      * Construct from a legacy point.
19333      */
19334      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19335#endif
19336
19337      std::string algo_name() const override;
19338
19339      AlgorithmIdentifier algorithm_identifier() const override;
19340};
19341
19342}
19343"#;
19344        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
19345        let declarations = parsed.declarations();
19346        let class = declarations
19347            .iter()
19348            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
19349            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19350        assert_eq!(
19351            parsed.raw_supertypes.get(class),
19352            Some(&vec!["BaseKey".to_string()]),
19353            "the virtual base is the class's base: {declarations:#?}"
19354        );
19355        for member in [
19356            "api.VirtualBased.algo_name",
19357            "api.VirtualBased.algorithm_identifier",
19358        ] {
19359            assert!(
19360                declarations
19361                    .iter()
19362                    .any(|unit| unit.is_function() && unit.fq_name() == member),
19363                "{member} must be owned by the recovered class: {declarations:#?}"
19364            );
19365        }
19366        assert!(
19367            declarations
19368                .iter()
19369                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19370            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
19371        );
19372    }
19373
19374    #[test]
19375    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
19376        // #2924: object-like macro lines before the head demote the `class`
19377        // keyword to a bare identifier and make the macro invocation an
19378        // `init_declarator`, so the head arrives as a `declaration` rather than
19379        // as an ERROR carrying a `class_specifier`. The name is still the last
19380        // identifier before `final`, and `virtual` is a base specifier rather
19381        // than a base.
19382        let source = r#"
19383namespace api {
19384
19385DIAGNOSTIC_PUSH
19386DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
19387
19388class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
19389                                               public virtual api::Inner::Key {
19390   public:
19391      std::string algo_name() const override;
19392};
19393
19394DIAGNOSTIC_POP
19395
19396}
19397"#;
19398        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
19399        let declarations = parsed.declarations();
19400        let class = declarations
19401            .iter()
19402            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
19403            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19404        assert_eq!(
19405            parsed.raw_supertypes.get(class),
19406            Some(&vec![
19407                "api::Outer::Key".to_string(),
19408                "api::Inner::Key".to_string()
19409            ]),
19410            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
19411        );
19412        assert!(
19413            declarations
19414                .iter()
19415                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
19416            "the member is owned by the recovered class: {declarations:#?}"
19417        );
19418        assert!(
19419            declarations
19420                .iter()
19421                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19422            "the macro invocation must not mint a declaration: {declarations:#?}"
19423        );
19424    }
19425
19426    #[test]
19427    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
19428        // The class embedded in a preceding malformed body follows the same
19429        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
19430        let fixture = |head: &str, name: &str| {
19431            format!(
19432                r#"
19433namespace api {{
19434class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
19435   public:
19436      /** Return a descriptive string. */
19437      const char* what() const noexcept override {{ return m_msg.c_str(); }}
19438
19439      /** Return the type of error. */
19440      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
19441
19442      /** Return an associated error code. */
19443      virtual int error_code() const noexcept {{ return 0; }}
19444
19445      /** Avoid throwing the base directly. */
19446      explicit Exception(std::string_view msg);
19447
19448      /** Avoid throwing the base directly. */
19449      Exception(const char* prefix, std::string_view msg);
19450
19451      /** Avoid throwing the base directly. */
19452      Exception(std::string_view msg, const std::exception& e);
19453
19454   private:
19455      std::string m_msg;
19456}};
19457
19458class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
19459   public:
19460      explicit {name}(std::string_view msg);
19461
19462      explicit {name}(std::string_view msg, std::string_view where);
19463
19464      {name}(std::string_view msg, const std::exception& e);
19465
19466      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
19467}};
19468}} // namespace api
19469"#
19470            )
19471        };
19472        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
19473            let source = fixture(head, name);
19474            let mut parser = Parser::new();
19475            parser
19476                .set_language(&tree_sitter_cpp::LANGUAGE.into())
19477                .expect("set C++ grammar");
19478            let tree = parser.parse(&source, None).expect("parse fixture");
19479            let mut embedded = Vec::new();
19480            let mut stack = vec![tree.root_node()];
19481            while let Some(node) = stack.pop() {
19482                embedded.extend(
19483                    recover_embedded_function_like_export_classes(node, &source)
19484                        .into_iter()
19485                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
19486                );
19487                let mut cursor = node.walk();
19488                stack.extend(node.named_children(&mut cursor));
19489            }
19490            assert!(
19491                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
19492                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
19493                tree.root_node().to_sexp()
19494            );
19495            assert!(
19496                embedded
19497                    .iter()
19498                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
19499                "{head}: the object-like macro is not a class: {embedded:#?}"
19500            );
19501
19502            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
19503            let declarations = parsed.declarations();
19504            let class = declarations
19505                .iter()
19506                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
19507                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
19508            assert_eq!(
19509                parsed.raw_supertypes.get(class),
19510                Some(&vec!["Exception".to_string()]),
19511                "{head}"
19512            );
19513            assert!(
19514                declarations
19515                    .iter()
19516                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
19517                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
19518            );
19519        }
19520    }
19521
19522    #[test]
19523    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
19524        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
19525        // `class MACRO(3, 6) Name final :` in the error, the first base as the
19526        // `type` of the following declaration, and closes that declaration with
19527        // a zero-width `MISSING identifier`. Reading the head by position must
19528        // not take that missing node for the class name: an empty name panics
19529        // at `FqName` construction, which took the Botan corpus replay down
19530        // (#2557). The head's tokens straddle the two nodes, so the positional
19531        // read spans both and recovers the class rather than leaving the
19532        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
19533        let source = r#"
19534namespace api {
19535class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
19536                                                    public virtual Botan::EC_PublicKey {
19537   public:
19538      std::string algo_name() const override { return "ECDSA"; }
19539};
19540} // namespace api
19541"#;
19542        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
19543        let declarations = parsed.declarations();
19544        assert!(
19545            declarations
19546                .iter()
19547                .all(|unit| !unit.identifier().is_empty()),
19548            "no declaration may carry an empty name: {declarations:#?}"
19549        );
19550        assert!(
19551            declarations
19552                .iter()
19553                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
19554            "macros and head specifiers must not become declarations: {declarations:#?}"
19555        );
19556        let class = declarations
19557            .iter()
19558            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
19559            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
19560        assert_eq!(
19561            parsed.raw_supertypes.get(class),
19562            Some(&vec![
19563                "Botan::TPM2::PublicKey".to_string(),
19564                "Botan::EC_PublicKey".to_string()
19565            ]),
19566            "both qualified virtual bases are bases: {declarations:#?}"
19567        );
19568        assert!(
19569            declarations
19570                .iter()
19571                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19572            "the head must not mint a macro-named class: {declarations:#?}"
19573        );
19574    }
19575
19576    #[test]
19577    fn function_like_export_class_survives_a_preceding_malformed_body() {
19578        let source = r#"
19579namespace api {
19580class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
19581   public:
19582      /** Return a descriptive string. */
19583      const char* what() const noexcept override { return m_msg.c_str(); }
19584
19585      /** Return the type of error. */
19586      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
19587
19588      /** Return an associated error code. */
19589      virtual int error_code() const noexcept { return 0; }
19590
19591      /** Avoid throwing the base directly. */
19592      explicit Exception(std::string_view msg);
19593
19594      /** Avoid throwing the base directly. */
19595      Exception(const char* prefix, std::string_view msg);
19596
19597      /** Avoid throwing the base directly. */
19598      Exception(std::string_view msg, const std::exception& e);
19599
19600   private:
19601      std::string m_msg;
19602};
19603
19604class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
19605   public:
19606      explicit Invalid_Argument(std::string_view msg);
19607
19608      explicit Invalid_Argument(std::string_view msg, std::string_view where);
19609
19610      Invalid_Argument(std::string_view msg, const std::exception& e);
19611
19612      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
19613};
19614} // namespace api
19615"#;
19616        let mut parser = Parser::new();
19617        parser
19618            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19619            .expect("set C++ grammar");
19620        let tree = parser.parse(source, None).expect("parse fixture");
19621        let mut stack = vec![tree.root_node()];
19622        let mut saw_embedded_shape = false;
19623        while let Some(node) = stack.pop() {
19624            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
19625                .iter()
19626                .any(|recovered| recovered.name == "Invalid_Argument");
19627            let mut cursor = node.walk();
19628            stack.extend(node.named_children(&mut cursor));
19629        }
19630        assert!(
19631            saw_embedded_shape,
19632            "fixture must retain the embedded error geometry: {}",
19633            tree.root_node().to_sexp()
19634        );
19635
19636        let parsed = parse_cpp_file(
19637            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
19638            source,
19639            &tree,
19640        );
19641        let declarations = parsed.declarations();
19642        let exception = declarations
19643            .iter()
19644            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
19645            .expect("qualified-base export class");
19646        let invalid = declarations
19647            .iter()
19648            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
19649            .expect("class embedded in the preceding malformed body");
19650
19651        assert_eq!(
19652            parsed.raw_supertypes.get(exception),
19653            Some(&vec!["std::exception".to_string()])
19654        );
19655        assert_eq!(
19656            parsed.raw_supertypes.get(invalid),
19657            Some(&vec!["Exception".to_string()])
19658        );
19659        assert!(
19660            parsed.materialization_records.iter().any(|record| matches!(
19661                record,
19662                MaterializationRecord::RecoveredDeclaration { unit, .. }
19663                    if unit == invalid
19664            )),
19665            "the embedded class must retain recovery provenance: {:#?}",
19666            parsed.materialization_records
19667        );
19668    }
19669
19670    #[test]
19671    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
19672        let source = r#"
19673public:
19674   explicit Lookup_Error(std::string_view err) : Exception(err) {}
19675
19676   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
19677"#;
19678        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
19679            .expect("reparse merged constructor body");
19680        let (range, body) =
19681            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
19682                .unwrap_or_else(|| {
19683                    panic!(
19684                        "the merged constructor must retain its structured declarator/body: {}",
19685                        tree.root_node().to_sexp()
19686                    )
19687                });
19688        assert_eq!(
19689            source.get(range).expect("constructor range"),
19690            "Lookup_Error(std::string_view err) : Exception(err) {}"
19691        );
19692        assert_eq!(node_text(body, source), "{}");
19693    }
19694
19695    #[test]
19696    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
19697        let positive_source =
19698            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
19699        let mut parser = tree_sitter::Parser::new();
19700        parser
19701            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19702            .unwrap();
19703        let positive_tree = parser.parse(positive_source, None).unwrap();
19704        assert!(cpp_reparsed_members_are_indexable(
19705            positive_tree.root_node(),
19706            positive_source
19707        ));
19708
19709        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
19710        let negative_tree = parser.parse(negative_source, None).unwrap();
19711        assert!(!cpp_reparsed_members_are_indexable(
19712            negative_tree.root_node(),
19713            negative_source
19714        ));
19715    }
19716
19717    #[test]
19718    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
19719        let copy_control_source = r#"
19720public:
19721    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
19722    explicit Token(const Token* tok);
19723    ~Token();
19724    Token* astOperand1() { return nullptr; }
19725"#;
19726        let constraint_source = r#"
19727private:
19728    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19729    static T *tokAtImpl(T *tok, int index) {
19730        return tok;
19731    }
19732
19733    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
19734    static T *linkAtImpl(T *tok, int index) {
19735        return tok;
19736    }
19737
19738public:
19739    int late() const { return 1; }
19740"#;
19741        let mut parser = tree_sitter::Parser::new();
19742        parser
19743            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19744            .unwrap();
19745        let copy_control_tree = parser
19746            .parse(copy_control_source, None)
19747            .expect("parse copy-control fixture");
19748        assert!(
19749            copy_control_tree.root_node().has_error(),
19750            "fixture must exercise adjacent copy-control recovery"
19751        );
19752        assert!(
19753            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
19754            "a complete late getter must remain recoverable after adjacent copy-control declarations"
19755        );
19756        let mut cursor = copy_control_tree.root_node().walk();
19757        assert!(
19758            copy_control_tree
19759                .root_node()
19760                .named_children(&mut cursor)
19761                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
19762            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
19763            copy_control_tree.root_node().to_sexp()
19764        );
19765        let constraint_tree = parser
19766            .parse(constraint_source, None)
19767            .expect("parse constraint-macro fixture");
19768        assert!(constraint_tree.root_node().has_error());
19769        assert!(
19770            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
19771            "complete constraint-macro members must not hide a later ordinary member"
19772        );
19773        let mut cursor = constraint_tree.root_node().walk();
19774        assert!(
19775            constraint_tree
19776                .root_node()
19777                .named_children(&mut cursor)
19778                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
19779                    child,
19780                    constraint_source
19781                )),
19782            "fixture must retain the split constraint-macro prefix/function geometry"
19783        );
19784    }
19785
19786    #[test]
19787    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
19788        let source = r#"
19789struct Analyzer {
19790    struct Action {
19791        Action() = default;
19792        Action(const Action&) = default;
19793        Action& operator=(const Action& rhs) & = default;
19794
19795        template<class T,
19796                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
19797                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
19798        // NOLINTNEXTLINE(google-explicit-constructor)
19799        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
19800        {}
19801
19802        enum : std::uint16_t { None = 0, Read = (1 << 0) };
19803        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
19804
19805    private:
19806        unsigned int mFlag{};
19807    };
19808
19809    enum class Direction : unsigned char { Forward, Reverse };
19810    virtual Action analyze(Direction d) const = 0;
19811};
19812"#;
19813        let mut parser = tree_sitter::Parser::new();
19814        parser
19815            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19816            .unwrap();
19817        let tree = parser.parse(source, None).unwrap();
19818        assert!(tree.root_node().has_error());
19819        let root = tree.root_node();
19820        let outer = root
19821            .named_children(&mut root.walk())
19822            .find(|child| child.kind() == "ERROR")
19823            .expect("fragmented Analyzer prefix");
19824        let outer_recovered =
19825            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
19826        assert_eq!(outer_recovered.name, "Analyzer");
19827        let outer_tree = cpp_reparse_fragmented_class_body(
19828            source,
19829            outer_recovered.body.reparse_start,
19830            outer_recovered.body.reparse_end,
19831        )
19832        .expect("reparse Analyzer body");
19833        let outer_root = outer_tree.root_node();
19834        let action_prefix = outer_root
19835            .named_children(&mut outer_root.walk())
19836            .find(|child| child.kind() == "ERROR")
19837            .expect("fragmented Action prefix");
19838        let action_recovered = fragmented_class_body(action_prefix, source)
19839            .expect("structured Action fragment boundary");
19840        assert_eq!(action_recovered.name, "Action");
19841        let action_tree = cpp_reparse_fragmented_class_body(
19842            source,
19843            action_recovered.body.reparse_start,
19844            action_recovered.body.reparse_end,
19845        )
19846        .expect("reparse Action body");
19847        let action_root = action_tree.root_node();
19848        let macro_prefix = action_root
19849            .named_children(&mut action_root.walk())
19850            .find(|child| child.kind() == "ERROR")
19851            .expect("constraint macro prefix");
19852        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
19853            .expect("structured template macro prefix");
19854        let macro_companion =
19855            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
19856        assert!(
19857            cpp_reparsed_template_macro_constructor_companion_is_indexable(
19858                macro_companion,
19859                macro_parameter,
19860                source,
19861            ),
19862            "split constrained constructor must be admitted: {}",
19863            macro_companion.to_sexp()
19864        );
19865        assert!(
19866            cpp_reparsed_members_are_indexable(action_root, source),
19867            "complete Action body must pass the recovery gate: {}",
19868            action_tree.root_node().to_sexp()
19869        );
19870        assert!(
19871            cpp_reparsed_members_are_indexable(outer_root, source),
19872            "complete Analyzer body must pass the recovery gate: {}",
19873            outer_tree.root_node().to_sexp()
19874        );
19875        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
19876        let parsed = parse_cpp_file(&file, source, &tree);
19877        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
19878            assert!(
19879                parsed
19880                    .declarations()
19881                    .iter()
19882                    .any(|unit| unit.fq_name() == expected),
19883                "missing recovered declaration {expected}: {:#?}",
19884                parsed.declarations()
19885            );
19886        }
19887        assert!(
19888            parsed
19889                .declarations()
19890                .iter()
19891                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
19892            "nested members must not remain flattened: {:#?}",
19893            parsed.declarations()
19894        );
19895    }
19896
19897    #[test]
19898    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
19899        let source = r#"
19900raw_hash_set& operator=(raw_hash_set&& that) {
19901  return move_assign(
19902      std::move(that),
19903      typename AllocTraits::propagate_on_container_move_assignment());
19904}
19905
19906iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
19907  return {};
19908}
19909
19910void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
19911
19912iterator insert(const_iterator hint, value_type&& value)
19913    ABSL_ATTRIBUTE_LIFETIME_BOUND {
19914  return {};
19915}
19916
19917friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
19918  return left.size() == right.size();
19919}
19920
19921static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
19922  return static_cast<slot_type*>(buffer);
19923}
19924
19925protected:
19926// Included-range recovery can attach this comment to the template prefix.
19927template <class K>
19928void AssertOnFind([[maybe_unused]] const K& key) {
19929  Check(key);
19930}
19931"#;
19932        let mut parser = tree_sitter::Parser::new();
19933        parser
19934            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19935            .unwrap();
19936        let tree = parser.parse(source, None).unwrap();
19937        assert!(
19938            tree.root_node().has_error(),
19939            "the fixture must exercise tree-sitter's errorful member shapes"
19940        );
19941        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
19942
19943        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
19944        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
19945        assert!(!cpp_reparsed_members_are_indexable(
19946            incomplete_tree.root_node(),
19947            incomplete_source
19948        ));
19949
19950        let outside_error_source = "int foo() stray_attribute {}\n";
19951        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
19952        assert!(outside_error_tree.root_node().has_error());
19953        assert!(!cpp_reparsed_members_are_indexable(
19954            outside_error_tree.root_node(),
19955            outside_error_source
19956        ));
19957
19958        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
19959        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
19960        assert!(!cpp_reparsed_members_are_indexable(
19961            variable_initializer_tree.root_node(),
19962            variable_initializer_source
19963        ));
19964    }
19965
19966    #[test]
19967    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
19968        let positive_source = r#"
19969std::pair<iterator, bool> insert(init_type&& value)
19970    ABSL_ATTRIBUTE_LIFETIME_BOUND
19971#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
19972  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
19973#endif
19974{
19975  return emplace(std::move(value));
19976}
19977"#;
19978        let mut parser = tree_sitter::Parser::new();
19979        parser
19980            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19981            .unwrap();
19982        let positive_tree = parser.parse(positive_source, None).unwrap();
19983        assert!(
19984            positive_tree.root_node().has_error(),
19985            "the fixture must exercise the split attribute/requires shape"
19986        );
19987        assert!(cpp_reparsed_members_are_indexable(
19988            positive_tree.root_node(),
19989            positive_source
19990        ));
19991
19992        let template_return_source = r#"
19993pair<int> insert(init_type&& value)
19994    ABSL_ATTRIBUTE_LIFETIME_BOUND
19995#if LANGUAGE_LEVEL >= 202002L
19996  requires(!Predicate<init_type>::value)
19997#endif
19998// Attributes and the function body may be separated by comments.
19999{
20000  return {};
20001}
20002"#;
20003        let template_return_tree = parser.parse(template_return_source, None).unwrap();
20004        assert!(
20005            cpp_reparsed_members_are_indexable(
20006                template_return_tree.root_node(),
20007                template_return_source
20008            ),
20009            "template-return attribute/requires tree: {}",
20010            template_return_tree.root_node().to_sexp()
20011        );
20012
20013        let no_body_source = r#"
20014std::pair<iterator, bool> insert(init_type&& value)
20015    ABSL_ATTRIBUTE_LIFETIME_BOUND
20016#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20017  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20018#endif
20019+ 0;
20020"#;
20021        let no_body_tree = parser.parse(no_body_source, None).unwrap();
20022        assert!(!cpp_reparsed_members_are_indexable(
20023            no_body_tree.root_node(),
20024            no_body_source
20025        ));
20026
20027        let extra_payload_source = r#"
20028pair<int> insert(init_type&& value)
20029    ABSL_ATTRIBUTE_LIFETIME_BOUND
20030#if LANGUAGE_LEVEL >= 202002L
20031  int unrelated;
20032  requires(Predicate<init_type>::value)
20033#endif
20034{
20035  return {};
20036}
20037"#;
20038        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20039        assert!(!cpp_reparsed_members_are_indexable(
20040            extra_payload_tree.root_node(),
20041            extra_payload_source
20042        ));
20043
20044        let variable_initializer_source = r#"
20045int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20046#if LANGUAGE_LEVEL >= 202002L
20047  requires(true)
20048#endif
20049{
20050  bad;
20051}
20052"#;
20053        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20054        assert!(!cpp_reparsed_members_are_indexable(
20055            variable_initializer_tree.root_node(),
20056            variable_initializer_source
20057        ));
20058    }
20059
20060    #[test]
20061    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20062        let source = r#"namespace absl {
20063ABSL_NAMESPACE_BEGIN namespace container_internal {
20064
20065class raw_hash_set : public Base {
20066 public:
20067  using value_type = int;
20068
20069  template <class U,
20070            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20071  void insert(U value) { (void)value; }
20072
20073  struct InsertSlot {
20074    raw_hash_set& s;
20075  };
20076};
20077
20078}
20079ABSL_NAMESPACE_END
20080}"#;
20081        let mut parser = tree_sitter::Parser::new();
20082        parser
20083            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20084            .unwrap();
20085        let tree = parser.parse(source, None).unwrap();
20086        let root = tree.root_node();
20087        let outer_namespace = root
20088            .named_children(&mut root.walk())
20089            .find(|child| child.kind() == "namespace_definition")
20090            .expect("outer absl namespace");
20091        let declaration_list = outer_namespace
20092            .child_by_field_name("body")
20093            .expect("outer namespace body");
20094        let sentinel_function = declaration_list
20095            .named_children(&mut declaration_list.walk())
20096            .find(|child| child.kind() == "function_definition")
20097            .expect("malformed namespace sentinel function");
20098        let ancestry = ParentIndex::new(root);
20099        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20100            .expect("structured nested namespace sentinel");
20101        let fragmented =
20102            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20103                .expect("fragmented raw_hash_set class");
20104        assert_eq!(fragmented.class_node.kind(), "ERROR");
20105        assert_eq!(fragmented.name, "raw_hash_set");
20106        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20107
20108        let outer_scope =
20109            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20110        let mut outer_siblings = Vec::new();
20111        push_cpp_sentinel_sibling_classes(
20112            &mut outer_siblings,
20113            declaration_list,
20114            sentinel.function,
20115            &outer_scope,
20116            source,
20117            &ancestry,
20118        );
20119        let [outer_shadow] = outer_siblings.as_slice() else {
20120            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20121        };
20122        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20123        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20124
20125        let field = "    raw_hash_set& s;";
20126        let start = source.find(field).expect("InsertSlot field") + 4;
20127        let node = root
20128            .descendant_for_byte_range(start, start + "raw_hash_set".len())
20129            .expect("raw_hash_set type node");
20130        let recovered = cpp_sentinel_recovered_classes(root, source);
20131        let [deep_class] = recovered.as_slice() else {
20132            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20133        };
20134        assert_eq!(
20135            deep_class.namespace_scope_components,
20136            vec!["absl", "container_internal"]
20137        );
20138        assert_eq!(
20139            deep_class.scope_components,
20140            vec!["absl", "container_internal", "raw_hash_set"]
20141        );
20142        assert!(
20143            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20144                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20145        );
20146
20147        assert_eq!(
20148            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20149            Some(vec![
20150                "absl".to_string(),
20151                "container_internal".to_string(),
20152                "raw_hash_set".to_string(),
20153                "InsertSlot".to_string(),
20154            ])
20155        );
20156
20157        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20158        let parsed = parse_cpp_file(&file, source, &tree);
20159        let raw_hash_set = parsed
20160            .declarations()
20161            .iter()
20162            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20163            .expect("recovered raw_hash_set class");
20164        assert_eq!(
20165            raw_hash_set.fq_name(),
20166            "absl::container_internal.raw_hash_set",
20167            "the recovered declaration must publish under the deeper sentinel namespace"
20168        );
20169        assert_eq!(
20170            parsed.raw_supertypes.get(raw_hash_set),
20171            Some(&vec!["Base".to_string()]),
20172            "the structured base clause on the fragmented ERROR prefix must survive publication"
20173        );
20174        assert!(
20175            parsed.materialization_records.iter().any(|record| matches!(
20176                record,
20177                MaterializationRecord::RecoveredDeclaration { recovery, unit }
20178                    if unit == raw_hash_set && *recovery == deep_class.class_range
20179            )),
20180            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20181            parsed.materialization_records
20182        );
20183    }
20184
20185    /// Issue #2358: recording an aggregate definition must not walk the whole
20186    /// file.
20187    ///
20188    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
20189    /// class-like shape that has a body, so the removal step runs once per
20190    /// aggregate. It used to `retain` over `top_level_declarations` and over
20191    /// *every* child list in the file on each of those calls, comparing whole
20192    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
20193    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
20194    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
20195    /// tens of thousands of times over and the C forward differential never
20196    /// finished.
20197    ///
20198    /// A definition the file has not already declared removes nothing, so the
20199    /// honest cost is zero regardless of how many other aggregates surround it.
20200    /// Two sizes an order of magnitude apart pin that the count is not merely
20201    /// small but independent of the file.
20202    ///
20203    /// The declaration walk answers every ancestor question from a
20204    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
20205    /// the root for each one (#2361). Substituting the index is only safe
20206    /// because it answers the identical question, so pin that on the shapes
20207    /// this file's recovery paths care about: anonymous and named aggregates,
20208    /// nested namespaces, templates, macro-displaced declarations and the
20209    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
20210    /// too -- `Node::parent` walks the visible tree, not the named one.
20211    #[test]
20212    fn the_parent_index_answers_what_tree_sitter_answers() {
20213        const SHAPES: [&str; 5] = [
20214            "namespace outer { namespace inner { struct Tag { int field; }; } }",
20215            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20216            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
20217            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20218            "class API Broken : public First, public Second {\n  void member();\n",
20219        ];
20220        for source in SHAPES {
20221            let mut parser = tree_sitter::Parser::new();
20222            parser
20223                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20224                .unwrap();
20225            let tree = parser.parse(source, None).unwrap();
20226            let root = tree.root_node();
20227            let ancestry = ParentIndex::new(root);
20228            let mut nodes = 0usize;
20229            let mut stack = vec![root];
20230            while let Some(node) = stack.pop() {
20231                nodes += 1;
20232                assert_eq!(
20233                    node.parent().map(|parent| parent.id()),
20234                    ancestry.parent(node).map(|parent| parent.id()),
20235                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20236                );
20237                let mut cursor = node.walk();
20238                stack.extend(node.children(&mut cursor));
20239            }
20240            assert!(nodes > 1, "{source:?} produced no tree to compare");
20241        }
20242    }
20243
20244    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
20245    /// index for every ancestor edge. Asking tree-sitter directly makes each
20246    /// edge re-descend from the root, turning declaration extraction on a
20247    /// deeply nested generated header from quadratic output work into a cubic
20248    /// tree walk. Exact query counts pin the route without a machine-dependent
20249    /// wall-clock ceiling.
20250    #[test]
20251    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20252        const DEPTH: usize = 64;
20253        let mut source = String::new();
20254        for level in 0..DEPTH {
20255            writeln!(source, "namespace n{level} {{").unwrap();
20256        }
20257        source.push_str("int deepest(int value);\n");
20258        for _ in 0..DEPTH {
20259            source.push_str("}\n");
20260        }
20261
20262        let mut parser = tree_sitter::Parser::new();
20263        parser
20264            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20265            .unwrap();
20266        let tree = parser.parse(&source, None).unwrap();
20267        let root = tree.root_node();
20268        let ancestry = ParentIndex::new(root);
20269        let mut function_declarator = None;
20270        walk_named_tree_preorder(root, true, |node| {
20271            if node.kind() == "function_declarator" {
20272                function_declarator = Some(node);
20273                WalkControl::Break
20274            } else {
20275                WalkControl::Continue
20276            }
20277        });
20278        let function_declarator = function_declarator.expect("deepest function declarator");
20279        let ancestor_count =
20280            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20281
20282        ancestry.reset_parent_query_count_for_test();
20283        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20284        assert_eq!(DEPTH, lexical_scope.len());
20285        assert_eq!(
20286            ancestor_count + 1,
20287            ancestry.parent_query_count_for_test(),
20288            "lexical-scope ancestry bypassed the parent index"
20289        );
20290
20291        ancestry.reset_parent_query_count_for_test();
20292        assert_eq!(
20293            DispatchExtensibility::Closed,
20294            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20295        );
20296        assert_eq!(
20297            ancestor_count,
20298            ancestry.parent_query_count_for_test(),
20299            "dispatch ancestry bypassed the parent index"
20300        );
20301
20302        ancestry.reset_parent_query_count_for_test();
20303        assert_eq!(
20304            CallableLinkage::External,
20305            cpp_callable_linkage(function_declarator, &source, &ancestry)
20306        );
20307        assert_eq!(
20308            ancestor_count + 1,
20309            ancestry.parent_query_count_for_test(),
20310            "linkage ancestry bypassed the parent index"
20311        );
20312
20313        ancestry.reset_parent_query_count_for_test();
20314        assert!(!cpp_callable_is_structural_constructor(
20315            function_declarator,
20316            &source,
20317            &ancestry
20318        ));
20319        assert_eq!(
20320            ancestor_count + 1,
20321            ancestry.parent_query_count_for_test(),
20322            "constructor ancestry bypassed the parent index"
20323        );
20324    }
20325
20326    /// Forward declarations followed by definitions are compacted as one
20327    /// batch, without rescanning the shared namespace/top-level lists for each
20328    /// tag. Definitions are intentionally visited in reverse order so the
20329    /// assertion also pins eager remove-and-reappend ordering.
20330    #[test]
20331    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
20332        for aggregates in [64usize, 512] {
20333            let mut source =
20334                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
20335            for index in 0..aggregates {
20336                writeln!(source, "struct tag{index};").unwrap();
20337            }
20338            for index in (0..aggregates).rev() {
20339                writeln!(
20340                    source,
20341                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
20342                )
20343                .unwrap();
20344            }
20345            source.push_str("}\n");
20346
20347            start_code_unit_removal_scan_probe();
20348            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
20349            let scanned = finish_code_unit_removal_scan_probe();
20350
20351            let expected_names: Vec<String> = (0..aggregates)
20352                .rev()
20353                .map(|index| format!("tag{index}"))
20354                .collect();
20355            let top_level_names: Vec<String> = parsed
20356                .top_level_declarations
20357                .iter()
20358                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20359                .map(|unit| unit.short_name().to_string())
20360                .collect();
20361            let namespace = parsed
20362                .declarations()
20363                .iter()
20364                .find(|unit| {
20365                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
20366                })
20367                .expect("generated namespace should be declared");
20368            let child_names: Vec<String> = parsed.children[namespace]
20369                .iter()
20370                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20371                .map(|unit| unit.short_name().to_string())
20372                .collect();
20373            assert_eq!(
20374                aggregates,
20375                parsed
20376                    .declarations()
20377                    .iter()
20378                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
20379                    .count(),
20380                "every aggregate must still be declared at {aggregates} aggregates"
20381            );
20382            assert_eq!(expected_names, top_level_names);
20383            assert_eq!(expected_names, child_names);
20384            assert_eq!(
20385                0, scanned,
20386                "replacing {aggregates} forward declarations must compact their shared lists once"
20387            );
20388        }
20389    }
20390
20391    #[test]
20392    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
20393        const DISTINCT_PER_KIND: usize = 64;
20394        let mut source = String::new();
20395        for index in 0..DISTINCT_PER_KIND {
20396            writeln!(source, "typedef int Alias{index};").unwrap();
20397        }
20398        writeln!(source, "typedef long Alias0;").unwrap();
20399        for index in 0..DISTINCT_PER_KIND {
20400            writeln!(source, "#define MACRO_{index} {index}").unwrap();
20401        }
20402        writeln!(source, "#define MACRO_0 duplicate").unwrap();
20403        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
20404
20405        let mut parser = tree_sitter::Parser::new();
20406        parser
20407            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20408            .unwrap();
20409        let tree = parser.parse(&source, None).unwrap();
20410        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
20411
20412        start_declaration_identity_comparison_probe();
20413        let parsed = parse_cpp_file(&file, &source, &tree);
20414        let comparisons = finish_declaration_identity_comparison_probe();
20415
20416        assert_eq!(
20417            DISTINCT_PER_KIND + 1,
20418            parsed
20419                .declarations()
20420                .iter()
20421                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
20422                .count(),
20423            "every physical typedef alias declaration must be retained so \
20424             conditional branch guards stay available to the resolver"
20425        );
20426        assert_eq!(
20427            DISTINCT_PER_KIND + 1,
20428            parsed
20429                .declarations()
20430                .iter()
20431                .filter(|unit| {
20432                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
20433                })
20434                .count(),
20435            "distinct macro redefinitions must remain available to temporal lookup"
20436        );
20437        assert_eq!(
20438            2,
20439            parsed
20440                .declarations()
20441                .iter()
20442                .filter(|unit| {
20443                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
20444                })
20445                .count(),
20446            "function overloads must remain distinct"
20447        );
20448
20449        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
20450        assert!(
20451            comparisons <= dedup_inputs * 4,
20452            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
20453        );
20454    }
20455
20456    #[test]
20457    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
20458        let source = r#"namespace absl {
20459ABSL_NAMESPACE_BEGIN namespace container_internal {
20460template <typename T>
20461class broken {
20462 public:
20463  using value_type = T;
20464  T operator->() const { return &operator*(); }
20465  using alias = value_type;
20466};
20467}
20468}
20469"#;
20470        let mut parser = tree_sitter::Parser::new();
20471        parser
20472            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20473            .unwrap();
20474        let tree = parser.parse(source, None).unwrap();
20475        let broken = find_class_named(tree.root_node(), source, "broken")
20476            .expect("the positive fixture must expose the broken class node");
20477        assert!(
20478            broken.has_error(),
20479            "the positive fixture must retain an internal parser error"
20480        );
20481        assert!(
20482            cpp_complete_class_body_close(broken).is_some(),
20483            "the positive fixture must expose a real class body close"
20484        );
20485        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20486        assert!(
20487            recovered.iter().any(|class| {
20488                class.scope_components == ["absl", "container_internal", "broken"]
20489            }),
20490            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
20491        );
20492    }
20493
20494    #[test]
20495    fn sentinel_recovery_keeps_members_after_nested_body_close() {
20496        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
20497NLOHMANN_BASIC_JSON_TPL_DECLARATION
20498class basic_json {
20499 private:
20500  union storage {
20501    int value;
20502  } data;
20503 public:
20504  using late_alias = int;
20505  late_alias value() const;
20506};
20507NLOHMANN_JSON_NAMESPACE_END
20508"#;
20509        let mut parser = tree_sitter::Parser::new();
20510        parser
20511            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20512            .unwrap();
20513        let tree = parser.parse(source, None).unwrap();
20514        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20515        let basic_json = recovered
20516            .iter()
20517            .find(|class| {
20518                class
20519                    .scope_components
20520                    .last()
20521                    .is_some_and(|name| name == "basic_json")
20522            })
20523            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
20524        let late_alias = source
20525            .find("late_alias value")
20526            .expect("late alias reference");
20527        assert!(
20528            basic_json.class_range.start_byte < late_alias
20529                && late_alias < basic_json.class_range.end_byte,
20530            "the recovered class range must include members after a nested close: {basic_json:#?}"
20531        );
20532    }
20533
20534    #[test]
20535    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
20536        let source = r#"namespace absl {
20537ABSL_NAMESPACE_BEGIN namespace container_internal {
20538template <typename T>
20539class broken {
20540 public:
20541  using value_type = T;
20542  T operator->() const { return &operator*(); }
20543}
20544}
20545"#;
20546        let mut parser = tree_sitter::Parser::new();
20547        parser
20548            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20549            .unwrap();
20550        let tree = parser.parse(source, None).unwrap();
20551        let broken = find_class_named(tree.root_node(), source, "broken")
20552            .expect("the negative fixture must expose the malformed class node");
20553        assert!(
20554            broken.has_error(),
20555            "the negative fixture must retain a parser error"
20556        );
20557        assert!(
20558            cpp_complete_class_body_close(broken).is_none(),
20559            "the malformed class must not expose a real body close"
20560        );
20561        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20562        assert!(
20563            recovered
20564                .iter()
20565                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
20566            "an incomplete class must not borrow the namespace close: {recovered:#?}"
20567        );
20568    }
20569
20570    #[test]
20571    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
20572        let source = r#"namespace absl {
20573ABSL_NAMESPACE_BEGIN namespace container_internal {
20574template <typename T>
20575struct broken {
20576  using value_type = T;
20577};
20578}
20579
20580#ifdef OWNER_DEF
20581template <typename T>
20582typename broken<T>::value_type broken<T>::method() {
20583  value_type value{};
20584  return value;
20585}
20586#endif
20587
20588namespace sibling {
20589template <typename T>
20590typename broken<T>::value_type broken<T>::other() {
20591  value_type value{};
20592  return value;
20593}
20594}
20595
20596ABSL_NAMESPACE_END
20597}
20598"#;
20599        let mut parser = tree_sitter::Parser::new();
20600        parser
20601            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20602            .unwrap();
20603        let tree = parser.parse(source, None).unwrap();
20604        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20605        let broken = recovered
20606            .iter()
20607            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20608            .expect("the sentinel class must be recovered");
20609        let method_start = source
20610            .find("typename broken<T>::value_type broken<T>::method()")
20611            .expect("guarded sibling owner");
20612        let method_end = source[method_start..]
20613            .find("\n}")
20614            .map(|offset| method_start + offset + 2)
20615            .expect("guarded sibling owner close");
20616        assert!(
20617            broken
20618                .owner_ranges
20619                .iter()
20620                .any(|owner| owner.range.start_byte <= method_start
20621                    && method_end <= owner.range.end_byte),
20622            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
20623        );
20624        let sibling_start = source
20625            .find("typename broken<T>::value_type broken<T>::other()")
20626            .expect("nested namespace sibling owner");
20627        assert!(
20628            broken
20629                .owner_ranges
20630                .iter()
20631                .all(|owner| owner.range.start_byte > sibling_start
20632                    || owner.range.end_byte <= sibling_start),
20633            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
20634        );
20635    }
20636
20637    #[test]
20638    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
20639        let source = r#"#ifdef OUTER
20640namespace absl {
20641ABSL_NAMESPACE_BEGIN namespace container_internal {
20642template <typename T>
20643struct broken {
20644  using value_type = T;
20645};
20646}
20647}
20648
20649#ifdef OWNER_DEF
20650template <typename T>
20651typename broken<T>::value_type broken<T>::method() {
20652  value_type value{};
20653  return value;
20654}
20655#endif
20656#endif
20657"#;
20658        let mut parser = tree_sitter::Parser::new();
20659        parser
20660            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20661            .unwrap();
20662        let tree = parser.parse(source, None).unwrap();
20663        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
20664        let broken = recovered
20665            .iter()
20666            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
20667            .expect("the sentinel class must be recovered");
20668        let method_start = source
20669            .find("typename broken<T>::value_type broken<T>::method()")
20670            .expect("outer sibling owner");
20671        assert!(
20672            broken
20673                .owner_ranges
20674                .iter()
20675                .all(|owner| owner.range.start_byte > method_start
20676                    || owner.range.end_byte <= method_start),
20677            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
20678        );
20679    }
20680
20681    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
20682    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
20683        let mut signatures = parsed
20684            .declarations()
20685            .iter()
20686            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
20687            .filter_map(|unit| unit.signature().map(str::to_string))
20688            .collect::<Vec<_>>();
20689        signatures.sort();
20690        signatures.dedup();
20691        signatures
20692    }
20693
20694    #[test]
20695    fn callable_parameter_types_come_from_the_ast_parameter_list() {
20696        let source = r#"
20697template <typename T, ENABLE_BYTES(T)>
20698Vec256<T> DupOdd(Vec256<T> value) { return value; }
20699
20700struct Visitor {
20701  void fail(this auto const& self) {}
20702};
20703"#;
20704        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
20705        let dup_odd = parsed
20706            .declarations()
20707            .iter()
20708            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
20709            .expect("DupOdd declaration");
20710        assert_eq!(
20711            dup_odd.signature(),
20712            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
20713        );
20714        assert_eq!(
20715            parsed
20716                .signature_metadata
20717                .get(dup_odd)
20718                .and_then(|metadata| metadata.first())
20719                .and_then(SignatureMetadata::callable_parameter_types),
20720            Some(["Vec256<T>".to_string()].as_slice())
20721        );
20722
20723        let fail = parsed
20724            .declarations()
20725            .iter()
20726            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
20727            .expect("explicit-object member");
20728        assert_eq!(fail.signature(), Some("(const this auto &)"));
20729        let metadata = parsed
20730            .signature_metadata
20731            .get(fail)
20732            .and_then(|metadata| metadata.first())
20733            .expect("explicit-object signature metadata");
20734        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
20735        assert!(
20736            metadata
20737                .callable_arity()
20738                .is_some_and(|arity| arity.accepts(0))
20739        );
20740    }
20741
20742    #[test]
20743    fn trailing_qualifiers_survive_parameter_list_whitespace() {
20744        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
20745        // declarator's structure, so an out-of-line definition that spells its
20746        // parameter list with different whitespace than the declaration must
20747        // still carry it.
20748        let source = r#"
20749struct Widget {
20750  bool multiline(int settings, int supprs) const;
20751  bool doublespace(int settings, int supprs) const;
20752  bool noexcept_multiline(int settings, int supprs) noexcept;
20753  bool ref_multiline(int settings, int supprs) &&;
20754};
20755bool
20756Widget::multiline (int settings,
20757                   int supprs) const
20758{ return settings + supprs > 0; }
20759bool Widget::doublespace(int settings,  int supprs) const { return true; }
20760bool Widget::noexcept_multiline(int settings,
20761                                int supprs) noexcept { return true; }
20762bool Widget::ref_multiline(int settings,
20763                           int supprs) && { return true; }
20764"#;
20765        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
20766        assert_eq!(
20767            vec!["(int, int) const".to_string()],
20768            identity_signatures(&parsed, "Widget.multiline")
20769        );
20770        assert_eq!(
20771            vec!["(int, int) const".to_string()],
20772            identity_signatures(&parsed, "Widget.doublespace")
20773        );
20774        assert_eq!(
20775            vec!["(int, int) noexcept".to_string()],
20776            identity_signatures(&parsed, "Widget.noexcept_multiline")
20777        );
20778        assert_eq!(
20779            vec!["(int, int) &&".to_string()],
20780            identity_signatures(&parsed, "Widget.ref_multiline")
20781        );
20782    }
20783
20784    #[test]
20785    fn macro_fragmented_plain_class_keeps_following_member_signature() {
20786        let source = r#"
20787struct CString {};
20788class CMessage {
20789public:
20790  CString GetParams(unsigned int index, unsigned int length = -1) const
20791      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
20792    return GetParamsColon(index, length);
20793  }
20794  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
20795};
20796CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
20797  return {};
20798}
20799"#;
20800        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
20801        assert_eq!(
20802            vec!["(unsigned int, unsigned int) const".to_string()],
20803            identity_signatures(&parsed, "CMessage.GetParamsColon")
20804        );
20805    }
20806
20807    #[test]
20808    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
20809        let source = r#"
20810#pragma once
20811#define DEMO_DEPRECATED(message)
20812namespace demo {
20813struct Base {
20814    static int aligned(int value) { return value; }
20815    int legacy(int value) const
20816        DEMO_DEPRECATED("use replacement()") { return value; }
20817    int replacement() const;
20818    void run(int value);
20819};
20820struct OtherBase {
20821    void run(int value);
20822    static int aligned(int value) { return value; }
20823};
20824struct Derived : Base {};
20825struct Override : Base {
20826    void run(int value);
20827    static int aligned(int value) { return value; }
20828};
20829struct RecoveredOverride : Base {
20830    int legacy(int value) const
20831        DEMO_DEPRECATED("use replacement()") { return value; }
20832    void run(int value);
20833};
20834struct Hidden : Base {
20835    void run(int first, int second);
20836    static int aligned(int first, int second) { return first + second; }
20837};
20838struct Ambiguous : Base, OtherBase {};
20839}
20840struct Global {};
20841"#;
20842        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
20843        let declarations = parsed.declarations();
20844        let fq_names = declarations
20845            .iter()
20846            .map(|unit| unit.fq_name())
20847            .collect::<std::collections::BTreeSet<_>>();
20848
20849        for expected in [
20850            "demo.Base",
20851            "demo.Base.aligned",
20852            "demo.Base.legacy",
20853            "demo.Base.replacement",
20854            "demo.Base.run",
20855            "demo.Derived",
20856            "demo.OtherBase",
20857            "demo.Override",
20858            "demo.RecoveredOverride",
20859            "demo.Hidden",
20860            "demo.Ambiguous",
20861            "Global",
20862        ] {
20863            assert!(
20864                fq_names.contains(expected),
20865                "missing {expected} from namespaced macro fragment: {declarations:#?}"
20866            );
20867        }
20868        assert!(
20869            !fq_names.contains("Derived"),
20870            "following class escaped its namespace: {declarations:#?}"
20871        );
20872        assert!(
20873            !fq_names.contains("demo.Global"),
20874            "global class crossed the recovered namespace boundary: {declarations:#?}"
20875        );
20876    }
20877
20878    #[test]
20879    fn trailing_qualifiers_still_separate_genuine_overloads() {
20880        // The qualifier must keep distinguishing the real C++ overload sets it
20881        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
20882        let source = r#"
20883struct Widget {
20884  int* slot(int index);
20885  const int* slot(int index) const;
20886  int log(int severity) &;
20887  int log(int severity) &&;
20888};
20889"#;
20890        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
20891        assert_eq!(
20892            vec!["(int)".to_string(), "(int) const".to_string()],
20893            identity_signatures(&parsed, "Widget.slot")
20894        );
20895        assert_eq!(
20896            vec!["(int) &".to_string(), "(int) &&".to_string()],
20897            identity_signatures(&parsed, "Widget.log")
20898        );
20899    }
20900
20901    #[test]
20902    fn virtual_specifier_is_not_part_of_the_identity_signature() {
20903        // `override` never appears on the out-of-line definition, and C++ does
20904        // not make it part of the signature, so it must not split the identity.
20905        let source = r#"
20906struct Base {
20907  virtual void run(int value) const;
20908};
20909struct Widget : Base {
20910  void run(int value) const override;
20911};
20912void Widget::run(int value) const {}
20913"#;
20914        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
20915        assert_eq!(
20916            vec!["(int) const".to_string()],
20917            identity_signatures(&parsed, "Widget.run")
20918        );
20919    }
20920
20921    #[test]
20922    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
20923        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
20924        // the function type, so a declaration that spells `const int` and a
20925        // definition that spells `int` are one entity.
20926        let source = r#"
20927struct Widget {
20928  bool value_params(const int settings, const int supprs);
20929  void pointee_const(const int* p);
20930  void pointer_const(int* const p);
20931  void both_const(const int* const p);
20932  void reference_const(const int& p);
20933  void array_const(const int values[4]);
20934};
20935bool Widget::value_params(int settings, int supprs) { return true; }
20936void Widget::pointer_const(int* p) {}
20937void Widget::both_const(const int* p) {}
20938"#;
20939        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
20940        assert_eq!(
20941            vec!["(int, int)".to_string()],
20942            identity_signatures(&parsed, "Widget.value_params")
20943        );
20944        assert_eq!(
20945            vec!["(int *)".to_string()],
20946            identity_signatures(&parsed, "Widget.pointer_const")
20947        );
20948        assert_eq!(
20949            vec!["(const int *)".to_string()],
20950            identity_signatures(&parsed, "Widget.both_const")
20951        );
20952        // The const that is not top-level still distinguishes the type.
20953        assert_eq!(
20954            vec!["(const int *)".to_string()],
20955            identity_signatures(&parsed, "Widget.pointee_const")
20956        );
20957        assert_eq!(
20958            vec!["(const int &)".to_string()],
20959            identity_signatures(&parsed, "Widget.reference_const")
20960        );
20961        assert_eq!(
20962            vec!["(const int [4])".to_string()],
20963            identity_signatures(&parsed, "Widget.array_const")
20964        );
20965    }
20966
20967    #[test]
20968    fn top_level_parameter_const_still_separates_pointee_overloads() {
20969        let source = r#"
20970struct Widget {
20971  void take(const int* p);
20972  void take(int* p);
20973};
20974"#;
20975        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
20976        assert_eq!(
20977            vec!["(const int *)".to_string(), "(int *)".to_string()],
20978            identity_signatures(&parsed, "Widget.take")
20979        );
20980    }
20981
20982    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
20983        let mut parser = tree_sitter::Parser::new();
20984        parser
20985            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20986            .unwrap();
20987        let tree = parser.parse(source, None).unwrap();
20988        let start = source.find(callable_name).expect("callable declaration");
20989        let declarator =
20990            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
20991        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
20992    }
20993
20994    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
20995        let mut shapes = comparable_shapes(source, callable_name);
20996        assert_eq!(1, shapes.len(), "{shapes:?}");
20997        match shapes.remove(0) {
20998            CppComparableSlot::Shape(shape) => shape,
20999            other => panic!("expected a comparable shape, got {other:?}"),
21000        }
21001    }
21002
21003    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21004        let mut current = shape.root();
21005        loop {
21006            match shape.node(current) {
21007                CppComparableNode::Named { .. } => return shape.node(current),
21008                CppComparableNode::Pointer { inner, .. }
21009                | CppComparableNode::Reference { inner }
21010                | CppComparableNode::Array { inner } => current = *inner,
21011                CppComparableNode::Generic { base, .. } => current = *base,
21012            }
21013        }
21014    }
21015
21016    #[test]
21017    fn comparable_shape_keeps_pointee_const() {
21018        assert_ne!(
21019            sole_comparable_shape("void f(const char* p);", "f("),
21020            sole_comparable_shape("void f(char* p);", "f(")
21021        );
21022    }
21023
21024    #[test]
21025    fn comparable_shape_keeps_inner_pointer_const() {
21026        assert_ne!(
21027            sole_comparable_shape("void f(int** p);", "f("),
21028            sole_comparable_shape("void f(int* const* p);", "f(")
21029        );
21030    }
21031
21032    #[test]
21033    fn comparable_shape_drops_top_level_pointer_const() {
21034        assert_eq!(
21035            sole_comparable_shape("void f(int* const p);", "f("),
21036            sole_comparable_shape("void f(int* p);", "f(")
21037        );
21038    }
21039
21040    #[test]
21041    fn comparable_shape_drops_top_level_base_const() {
21042        assert_eq!(
21043            sole_comparable_shape("void f(const int p);", "f("),
21044            sole_comparable_shape("void f(int p);", "f(")
21045        );
21046    }
21047
21048    #[test]
21049    fn comparable_shape_decays_top_level_array_to_pointer() {
21050        assert_eq!(
21051            sole_comparable_shape("void f(int a[3]);", "f("),
21052            sole_comparable_shape("void f(int* a);", "f(")
21053        );
21054        assert_eq!(
21055            sole_comparable_shape("void f(int* a[3]);", "f("),
21056            sole_comparable_shape("void f(int** a);", "f(")
21057        );
21058    }
21059
21060    #[test]
21061    fn comparable_shape_keeps_array_behind_pointer() {
21062        assert_ne!(
21063            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21064            sole_comparable_shape("struct S { void f(int** a); };", "f(")
21065        );
21066    }
21067
21068    #[test]
21069    fn comparable_shape_records_written_name_and_lexical_scope() {
21070        let declared =
21071            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21072        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21073        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21074            panic!("named leaf");
21075        };
21076        assert_eq!(["Msg".to_string()].as_slice(), name.path());
21077        assert_eq!(
21078            ["ns".to_string(), "S".to_string()].as_slice(),
21079            name.lexical_scope()
21080        );
21081        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21082            panic!("named leaf");
21083        };
21084        assert_eq!(
21085            ["ns".to_string(), "Msg".to_string()].as_slice(),
21086            name.path()
21087        );
21088        assert!(name.lexical_scope().is_empty());
21089        assert_ne!(declared, defined);
21090    }
21091
21092    #[test]
21093    fn comparable_shape_marks_sized_primitive_leaf() {
21094        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21095        let CppComparableNode::Named {
21096            name, primitive, ..
21097        } = comparable_named_leaf(&shape)
21098        else {
21099            panic!("named leaf");
21100        };
21101        assert!(primitive);
21102        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21103        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21104    }
21105
21106    #[test]
21107    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21108        assert_eq!(
21109            vec![CppComparableSlot::Unstructured],
21110            comparable_shapes("void f(void (*cb)(int));", "f(")
21111        );
21112    }
21113
21114    #[test]
21115    fn comparable_shape_reports_ellipsis_slot() {
21116        let shapes = comparable_shapes("void f(int a, ...);", "f(");
21117        assert_eq!(2, shapes.len(), "{shapes:?}");
21118        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21119    }
21120
21121    #[test]
21122    fn comparable_shape_keeps_template_argument_const() {
21123        assert_ne!(
21124            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21125            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21126        );
21127    }
21128
21129    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
21130    /// file-scope tag that a later `struct inner *` at file scope may name.
21131    #[test]
21132    fn c_file_mints_aggregate_member_tag_at_file_scope() {
21133        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21134        let parsed = parse_cpp_declarations(source, "x.c");
21135        let declarations = parsed.declarations();
21136
21137        assert!(
21138            declarations
21139                .iter()
21140                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21141            "expected a file-scope inner tag, got {declarations:?}"
21142        );
21143        assert!(
21144            declarations
21145                .iter()
21146                .all(|unit| unit.fq_name() != "outer$inner"),
21147            "expected no nested identity, got {declarations:?}"
21148        );
21149        assert!(
21150            declarations
21151                .iter()
21152                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21153        );
21154        // Members still belong to their own aggregate.
21155        assert!(
21156            declarations
21157                .iter()
21158                .any(|unit| unit.fq_name() == "inner.value")
21159        );
21160        assert!(
21161            declarations
21162                .iter()
21163                .any(|unit| unit.fq_name() == "outer.item")
21164        );
21165
21166        let outer = declarations
21167            .iter()
21168            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21169            .expect("outer");
21170        assert!(
21171            parsed
21172                .children
21173                .get(outer)
21174                .into_iter()
21175                .flatten()
21176                .all(|child| child.fq_name() != "inner"),
21177            "the tag must not hang off the aggregate it is written inside: {:?}",
21178            parsed.children
21179        );
21180    }
21181
21182    /// A header carries no compilation language of its own, and a `.cpp`
21183    /// translation unit really does declare a nested class. Both keep exactly
21184    /// the C++ extraction they had before the C dialect existed.
21185    #[test]
21186    fn header_and_cpp_files_keep_nested_tag_identity() {
21187        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21188        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21189            let parsed = parse_cpp_declarations(source, name);
21190            let declarations = parsed.declarations();
21191            assert!(
21192                declarations
21193                    .iter()
21194                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21195                "{name} must keep the nested identity, got {declarations:?}"
21196            );
21197            assert!(
21198                declarations.iter().all(|unit| unit.fq_name() != "inner"),
21199                "{name} must not mint a file-scope tag, got {declarations:?}"
21200            );
21201            assert!(
21202                declarations
21203                    .iter()
21204                    .any(|unit| unit.fq_name() == "outer$inner.value")
21205            );
21206        }
21207    }
21208
21209    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
21210    #[test]
21211    fn uppercase_c_extension_keeps_cpp_tag_scope() {
21212        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21213        let parsed = parse_cpp_declarations(source, "x.C");
21214        assert!(
21215            parsed
21216                .declarations()
21217                .iter()
21218                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21219        );
21220    }
21221
21222    /// There is no such thing as a partially nested tag in C: every level of a
21223    /// nested aggregate chain lands at the same enclosing scope.
21224    #[test]
21225    fn c_file_mints_every_nesting_level_at_file_scope() {
21226        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21227        let parsed = parse_cpp_declarations(source, "z.c");
21228        let declarations = parsed.declarations();
21229
21230        for tag in ["a", "b", "c"] {
21231            assert!(
21232                declarations
21233                    .iter()
21234                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
21235                "expected a file-scope {tag}, got {declarations:?}"
21236            );
21237        }
21238        assert!(
21239            declarations
21240                .iter()
21241                .all(|unit| !unit.fq_name().contains('$')),
21242            "no level may keep a nested identity, got {declarations:?}"
21243        );
21244        // Each member still belongs to the aggregate that declares it.
21245        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21246        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21247        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21248    }
21249
21250    /// An enum tag is a tag; its enumerators stay members of the enum, which is
21251    /// what makes them ordinary identifiers at the enum's own (file) scope.
21252    #[test]
21253    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21254        let source = "struct outer { enum color { RED, GREEN } c; };\n";
21255        let parsed = parse_cpp_declarations(source, "e.c");
21256        let declarations = parsed.declarations();
21257
21258        let color = declarations
21259            .iter()
21260            .find(|unit| unit.is_class() && unit.fq_name() == "color")
21261            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21262        assert!(
21263            declarations
21264                .iter()
21265                .all(|unit| unit.fq_name() != "outer$color")
21266        );
21267        for enumerator in ["color.RED", "color.GREEN"] {
21268            assert!(
21269                declarations.iter().any(|unit| unit.fq_name() == enumerator),
21270                "expected {enumerator}, got {declarations:?}"
21271            );
21272        }
21273        let children = parsed
21274            .children
21275            .get(color)
21276            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21277        assert!(
21278            ["color.RED", "color.GREEN"]
21279                .iter()
21280                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21281            "enumerators must hang off their enum: {children:?}"
21282        );
21283    }
21284
21285    #[test]
21286    fn c_file_mints_member_list_union_at_file_scope() {
21287        let source = "struct outer { union inner { int a; float b; } item; };\n";
21288        let parsed = parse_cpp_declarations(source, "u.c");
21289        let declarations = parsed.declarations();
21290        assert!(
21291            declarations
21292                .iter()
21293                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21294            "expected a file-scope inner union, got {declarations:?}"
21295        );
21296        assert!(
21297            declarations
21298                .iter()
21299                .all(|unit| unit.fq_name() != "outer$inner")
21300        );
21301        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
21302        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
21303    }
21304
21305    /// A tag declared in a namespace member list is not a file-scope tag: the
21306    /// nearest enclosing non-aggregate scope is the namespace.
21307    #[test]
21308    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
21309        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
21310        let parsed = parse_cpp_declarations(source, "n.c");
21311        let declarations = parsed.declarations();
21312        let inner = declarations
21313            .iter()
21314            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
21315            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
21316        assert_eq!(inner.package_name(), "ns");
21317        assert!(
21318            declarations
21319                .iter()
21320                .all(|unit| unit.fq_name() != "ns.outer$inner")
21321        );
21322    }
21323
21324    /// Pins today's treatment of a tag declared inside a function body: the
21325    /// declaration walk does not descend into statement bodies, so no unit is
21326    /// minted for it in either dialect. C block scope is out of scope for the
21327    /// dialect change, and this test proves the change did not disturb it.
21328    #[test]
21329    fn function_local_tags_are_unchanged_in_both_dialects() {
21330        let source =
21331            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
21332        for name in ["y.c", "y.cpp"] {
21333            let parsed = parse_cpp_declarations(source, name);
21334            let declarations = parsed.declarations();
21335            assert!(
21336                declarations
21337                    .iter()
21338                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
21339                "{name}: {declarations:?}"
21340            );
21341            for tag in ["localtag", "deeper", "localtag$deeper"] {
21342                assert!(
21343                    declarations.iter().all(|unit| unit.fq_name() != tag),
21344                    "{name} must not mint {tag}, got {declarations:?}"
21345                );
21346            }
21347        }
21348    }
21349
21350    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
21351    /// re-scope: the typedef name is identical in both dialects.
21352    #[test]
21353    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
21354        let source = "typedef struct { int v; } T;\n";
21355        for name in ["t.c", "t.cpp"] {
21356            let parsed = parse_cpp_declarations(source, name);
21357            let declarations = parsed.declarations();
21358            assert!(
21359                declarations
21360                    .iter()
21361                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
21362                "{name}: {declarations:?}"
21363            );
21364        }
21365    }
21366
21367    #[test]
21368    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
21369        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
21370        let parsed = parse_cpp_declarations(source, "socket.c");
21371        let declarations = parsed.declarations();
21372        assert_eq!(
21373            declarations
21374                .iter()
21375                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
21376                .count(),
21377            1,
21378            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
21379        );
21380        for expected in [
21381            "PAL_HANDLE",
21382            "PAL_HANDLE.sock",
21383            "PAL_HANDLE$sock",
21384            "PAL_HANDLE$sock.ops",
21385        ] {
21386            assert!(
21387                declarations.iter().any(|unit| unit.fq_name() == expected),
21388                "expected {expected}, got {declarations:?}"
21389            );
21390        }
21391    }
21392
21393    /// `class` is not C. Source that spells one in a `.c` file is not C code,
21394    /// so it keeps the C++ reading rather than acquiring a half-C identity.
21395    #[test]
21396    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
21397        let source = "class outer { class inner { int v; }; };\n";
21398        let c_parsed = parse_cpp_declarations(source, "k.c");
21399        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
21400        let c_declarations = c_parsed.declarations();
21401        let cpp_declarations = cpp_parsed.declarations();
21402        assert!(
21403            c_declarations
21404                .iter()
21405                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21406            "{c_declarations:?}"
21407        );
21408        assert_eq!(
21409            c_declarations
21410                .iter()
21411                .map(|unit| unit.fq_name())
21412                .collect::<std::collections::BTreeSet<_>>(),
21413            cpp_declarations
21414                .iter()
21415                .map(|unit| unit.fq_name())
21416                .collect::<std::collections::BTreeSet<_>>()
21417        );
21418    }
21419
21420    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
21421    /// every (node, class-like name) pair a tree offers, and require the same
21422    /// answer from both.
21423    ///
21424    /// The release build has no `debug_assertions` agreement check, so this is
21425    /// what pins the two together there.  Both query orders are exercised:
21426    /// document order is what the walk does, and reverse order proves that a
21427    /// question about an earlier byte than one already answered is still
21428    /// filtered back to its own prefix rather than answered from the wider
21429    /// fold.
21430    ///
21431    /// Returns how many questions were answered with a namespace, so a fixture
21432    /// can assert it actually reached the path (#2754).
21433    fn namespace_forward_scan_agreement(source: &str) -> usize {
21434        let mut parser = tree_sitter::Parser::new();
21435        parser
21436            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21437            .unwrap();
21438        let tree = parser.parse(source, None).unwrap();
21439        let root = tree.root_node();
21440        let ancestry = ParentIndex::new(root);
21441
21442        let mut nodes = Vec::new();
21443        let mut names = std::collections::BTreeSet::new();
21444        let mut cursor = root.walk();
21445        let mut stack = vec![root];
21446        while let Some(node) = stack.pop() {
21447            if matches!(
21448                node.kind(),
21449                "class_specifier" | "struct_specifier" | "union_specifier"
21450            ) && let Some(name) = class_like_name(node, source, &ancestry)
21451            {
21452                names.insert(name);
21453            }
21454            nodes.push(node);
21455            stack.extend(node.named_children(&mut cursor));
21456        }
21457        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
21458        assert!(!names.is_empty(), "fixture declares no class-like name");
21459
21460        let mut answered = 0usize;
21461        for reversed in [false, true] {
21462            let mut scan = CppNamespaceForwardScan::default();
21463            let ordered: Vec<_> = if reversed {
21464                nodes.iter().rev().copied().collect()
21465            } else {
21466                nodes.clone()
21467            };
21468            answered = 0;
21469            for node in ordered {
21470                for name in &names {
21471                    scan.advance_to(root, node.start_byte(), source, &ancestry);
21472                    let carried = scan.unique_earlier_forward(name, node);
21473                    assert_eq!(
21474                        carried,
21475                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
21476                        "carried-forward scan and prefix scan disagree about {name} at \
21477                         {} node starting at byte {} (reversed order: {reversed})",
21478                        node.kind(),
21479                        node.start_byte()
21480                    );
21481                    answered += usize::from(carried.is_some());
21482                }
21483            }
21484        }
21485        answered
21486    }
21487
21488    /// A malformed namespace whose forward declarations are the only identity
21489    /// signal left for the class definitions tree-sitter pushed out to file
21490    /// scope.  Both recovered classes open the guard; only the first one is
21491    /// separated from the namespace by nothing but recovery trivia, so only the
21492    /// first one borrows.  The carried-forward scan has to reproduce both
21493    /// answers.
21494    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
21495namespace ns {
21496class Widget;
21497class Gadget;
21498int x = ;
21499}
21500class API Widget {
21501public:
21502    void first();
21503};
21504class API Gadget {
21505public:
21506    void second();
21507};
21508"#;
21509
21510    #[test]
21511    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
21512        assert!(
21513            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
21514            "the fixture must actually reach the namespace-borrow path"
21515        );
21516
21517        // Nothing here may be answered, and the two paths have to agree about
21518        // that too: a clean namespace is not an identity proof, two forwards of
21519        // one name are ambiguous rather than a guess, and a forward inside a
21520        // function body is not at namespace scope.
21521        for source in [
21522            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
21523            r#"#define API
21524namespace ns {
21525class Widget;
21526class Widget;
21527int x = ;
21528}
21529class API Widget {
21530public:
21531    void method();
21532};
21533"#,
21534            r#"#define API
21535namespace ns {
21536void host() {
21537    class Widget;
21538}
21539int x = ;
21540}
21541class API Widget {
21542public:
21543    void method();
21544};
21545"#,
21546        ] {
21547            assert_eq!(
21548                namespace_forward_scan_agreement(source),
21549                0,
21550                "no borrow is justified here: {source}"
21551            );
21552        }
21553    }
21554
21555    /// The fold is incremental, so a walk that asks about steadily later bytes
21556    /// must never re-fold a node an earlier question already folded, and must
21557    /// never skip one that lies between two questions.
21558    #[test]
21559    fn carried_forward_namespace_scan_folds_each_node_once() {
21560        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
21561        let mut parser = tree_sitter::Parser::new();
21562        parser
21563            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21564            .unwrap();
21565        let tree = parser.parse(source, None).unwrap();
21566        let root = tree.root_node();
21567        let ancestry = ParentIndex::new(root);
21568
21569        let mut incremental = CppNamespaceForwardScan::default();
21570        for cutoff in 0..=source.len() {
21571            incremental.advance_to(root, cutoff, source, &ancestry);
21572        }
21573        let mut whole = CppNamespaceForwardScan::default();
21574        whole.advance_to(root, source.len(), source, &ancestry);
21575
21576        let mut incremental_shape: Vec<_> = incremental
21577            .forwards
21578            .iter()
21579            .map(|(name, forwards)| {
21580                (
21581                    name.clone(),
21582                    forwards
21583                        .iter()
21584                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21585                        .collect::<Vec<_>>(),
21586                )
21587            })
21588            .collect();
21589        let mut whole_shape: Vec<_> = whole
21590            .forwards
21591            .iter()
21592            .map(|(name, forwards)| {
21593                (
21594                    name.clone(),
21595                    forwards
21596                        .iter()
21597                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
21598                        .collect::<Vec<_>>(),
21599                )
21600            })
21601            .collect();
21602        incremental_shape.sort();
21603        whole_shape.sort();
21604        for (_, forwards) in &mut incremental_shape {
21605            forwards.sort();
21606        }
21607        for (_, forwards) in &mut whole_shape {
21608            forwards.sort();
21609        }
21610
21611        assert!(!whole_shape.is_empty(), "fixture folds no forward");
21612        assert_eq!(
21613            incremental_shape, whole_shape,
21614            "one byte at a time must fold exactly what one whole pass folds"
21615        );
21616    }
21617
21618    /// Drive the region reparse and the whitespace-padded reparse it replaced
21619    /// over the same region and require identical trees.
21620    ///
21621    /// The release build has no `debug_assertions` agreement check, so this is
21622    /// what pins the two together there. Each body is reparsed at its own
21623    /// offset and again after a long prefix, because the prefix is the whole
21624    /// difference between the two techniques: the padded parse lexes it as
21625    /// whitespace, the included-range parse never sees it, and the tree has to
21626    /// come out the same either way (#2788).
21627    fn fragmented_class_reparse_agreement(body: &str) {
21628        for prefix in [
21629            String::new(),
21630            "// leading comment\n".to_string(),
21631            // A body starts just after its class head's `{`, which is normally
21632            // mid-line: the padded parse then has spaces before the region on
21633            // the region's own line, and the included-range parse has nothing
21634            // at all before it.
21635            "class Widget : public Base { ".to_string(),
21636            "namespace filler {\n".to_string()
21637                + &"struct Filler { int member; };\n".repeat(200)
21638                + "}\n",
21639            "namespace filler {\n".to_string()
21640                + &"struct Filler { int member; };\n".repeat(200)
21641                + "}\nclass Widget : public Base { ",
21642        ] {
21643            let source = format!("{prefix}{body}");
21644            let start = prefix.len();
21645            let end = source.len();
21646            let region = cpp_reparse_fragmented_class_body(&source, start, end)
21647                .expect("the region reparse must produce a tree");
21648            let padded = cpp_reparse_padded_class_body(&source, start, end)
21649                .expect("the padded reparse must produce a tree");
21650            assert_eq!(
21651                cpp_tree_shape(&region),
21652                cpp_tree_shape(&padded),
21653                "region and padded reparse disagree at offset {start} of {end} bytes"
21654            );
21655            assert_eq!(
21656                region.root_node().start_byte(),
21657                start,
21658                "the reparsed region keeps its original offsets"
21659            );
21660        }
21661    }
21662
21663    #[test]
21664    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
21665        // A conditional immediately after an access label: the shape the padded
21666        // technique was kept for, because the directive and its macro name
21667        // become an ERROR plus the following declaration's apparent type.
21668        fragmented_class_reparse_agreement(
21669            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
21670        );
21671        fragmented_class_reparse_agreement(
21672            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
21673        );
21674        // The merged inline constructor and the nested fragmented bodies the
21675        // #938 recovery reads out of a reparse.
21676        fragmented_class_reparse_agreement(
21677            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
21678        );
21679        fragmented_class_reparse_agreement(
21680            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
21681        );
21682        // A body that is not member-shaped at all still has to reparse the same
21683        // way, because the admission gate reads the tree to reject it.
21684        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
21685    }
21686
21687    /// A header shaped like the generated ones this walk is slow on: many
21688    /// enums, classes whose members share the enums' names, nested enums, an
21689    /// ownerless enumerator, and namespaced repeats of all of it.
21690    fn many_enums_and_mixed_declarations() -> String {
21691        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
21692        for index in 0..40 {
21693            let _ = write!(
21694                source,
21695                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
21696                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
21697                 class Color{index}Like {{ public: int member{index}; }};\n"
21698            );
21699        }
21700        source.push_str("namespace outer {\n");
21701        for index in 0..20 {
21702            let _ = write!(
21703                source,
21704                "enum Shade{index} {{ DARK{index} }};\n\
21705                 struct Shade{index}Holder {{ int field{index}; }};\n"
21706            );
21707        }
21708        source.push_str("}\n");
21709        source
21710    }
21711
21712    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
21713    /// every question a fixture's declarations can ask, and require the same
21714    /// answer from both.
21715    ///
21716    /// The release build has no `debug_assertions` agreement check, so this is
21717    /// what pins the two together there. The index is fed one declaration at a
21718    /// time and every question is re-asked after each one, which is what proves
21719    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
21720    /// test. Each of the fixture's own fields is also fed in restated as a
21721    /// declaration of another file: the scan ignores those because it asks
21722    /// about the asking unit's own source, and the index has to ignore them for
21723    /// the same reason.
21724    ///
21725    /// Returns how many questions the fixture answered `true`, so a caller can
21726    /// assert that it actually reached the path (#2786).
21727    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
21728        let parsed = parse_cpp_declarations(source, name);
21729        let file = ProjectFile::new(std::env::temp_dir(), name);
21730        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
21731
21732        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
21733        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
21734
21735        let foreign: Vec<CodeUnit> = declarations
21736            .iter()
21737            .filter(|unit| unit.kind() == CodeUnitType::Field)
21738            .map(|unit| {
21739                CodeUnit::new_fq(
21740                    elsewhere.clone(),
21741                    unit.kind(),
21742                    unit.package_name().to_string(),
21743                    unit.short_name().to_string(),
21744                    unit.fq().clone(),
21745                )
21746            })
21747            .collect();
21748
21749        // One owner chain deeper than any C++ short name reaches today
21750        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
21751        // the member). The scan asks `starts_with("owner.")`, so a field like
21752        // this answers for every owner in its chain, and the index has to
21753        // record every one of them rather than only the innermost.
21754        let mut packages: Vec<String> = declarations
21755            .iter()
21756            .map(|unit| unit.package_name().to_string())
21757            .collect();
21758        packages.push(String::new());
21759        packages.sort();
21760        packages.dedup();
21761        let deeper: Vec<CodeUnit> = packages
21762            .iter()
21763            .map(|package_name| {
21764                CodeUnit::new_fq(
21765                    file.clone(),
21766                    CodeUnitType::Field,
21767                    package_name.clone(),
21768                    "SynthOwner.middle.leaf".to_string(),
21769                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
21770                )
21771            })
21772            .collect();
21773
21774        // Every (package, owner) pair anything could ask about: each unit's own
21775        // short name, each dotted prefix of it, and the empty owner an
21776        // anonymous enum asks with (#2140).
21777        let mut questions: Vec<(String, String)> = Vec::new();
21778        for unit in declarations.iter().chain(deeper.iter()) {
21779            let package_name = unit.package_name().to_string();
21780            let short_name = unit.short_name();
21781            questions.push((package_name.clone(), short_name.to_string()));
21782            questions.push((package_name.clone(), String::new()));
21783            for (offset, _) in short_name.match_indices('.') {
21784                questions.push((package_name.clone(), short_name[..offset].to_string()));
21785            }
21786        }
21787        questions.sort();
21788        questions.dedup();
21789
21790        let mut index = CppFieldOwnerIndex::default();
21791        let mut recorded: Vec<&CodeUnit> = Vec::new();
21792        let mut answered = 0usize;
21793        for unit in foreign
21794            .iter()
21795            .chain(declarations.iter())
21796            .chain(deeper.iter())
21797        {
21798            index.record(unit, &file);
21799            recorded.push(unit);
21800            for (package_name, owner_short_name) in &questions {
21801                let carried = index.owns_fields(package_name, owner_short_name);
21802                assert_eq!(
21803                    carried,
21804                    cpp_declarations_hold_owned_fields(
21805                        recorded.iter().copied(),
21806                        &file,
21807                        package_name,
21808                        owner_short_name
21809                    ),
21810                    "the carried field index and the declaration scan disagree about \
21811                     {package_name:?}/{owner_short_name:?} after recording {}",
21812                    unit.fq_name()
21813                );
21814                answered += usize::from(carried);
21815            }
21816        }
21817
21818        // The whole-set build the first question performs must land on the same
21819        // index the incremental record built.
21820        let rebuilt = CppFieldOwnerIndex::of(
21821            foreign
21822                .iter()
21823                .chain(declarations.iter())
21824                .chain(deeper.iter()),
21825            &file,
21826        );
21827        for (package_name, owner_short_name) in &questions {
21828            assert_eq!(
21829                rebuilt.owns_fields(package_name, owner_short_name),
21830                index.owns_fields(package_name, owner_short_name),
21831                "a rebuilt index must answer what the incremental one answers for \
21832                 {package_name:?}/{owner_short_name:?}"
21833            );
21834        }
21835        answered
21836    }
21837
21838    /// The one thing the field index cannot absorb by addition: a deferred
21839    /// replacement of a declaration that owns children removes those children.
21840    ///
21841    /// The first enum builds the index, the struct records `Color.RED` into it,
21842    /// the body-less second `Color` replaces the first and takes `Color.RED`
21843    /// with it, and the last enum then asks about owner `Color`. An index that
21844    /// survived that removal answers `true` where the declarations say `false`,
21845    /// which is exactly what the in-walk agreement assertion catches (#2786).
21846    #[test]
21847    fn a_replacement_that_removes_children_drops_the_field_index() {
21848        let source =
21849            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
21850        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
21851        let mut names: Vec<_> = parsed
21852            .declarations()
21853            .iter()
21854            .map(|unit| unit.fq_name())
21855            .collect();
21856        names.sort();
21857        assert_eq!(
21858            names,
21859            vec![
21860                "Color".to_string(),
21861                "First".to_string(),
21862                "First.A".to_string()
21863            ],
21864            "the replaced Color owns no field any more"
21865        );
21866    }
21867
21868    /// A recovery that re-declares what the file already declared mints
21869    /// nothing.
21870    ///
21871    /// The reparse walk replaces the outer `Widget`, which removes its method,
21872    /// and then re-creates that method from the region. Creation alone would
21873    /// call the method recovered; it was there before the recovery opened, so
21874    /// the recovered set is empty and only the region's own reparse window is
21875    /// recorded (#2787).
21876    #[test]
21877    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
21878        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
21879                      BEGIN_NS\n\
21880                      namespace demo { struct Widget { void doWork(); }; }\n\
21881                      END_NS\n";
21882        let parsed = parse_cpp_declarations(source, "restored.cpp");
21883        let recovered: Vec<String> = parsed
21884            .materialization_records
21885            .iter()
21886            .filter_map(|record| match record {
21887                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
21888                _ => None,
21889            })
21890            .collect();
21891        assert!(
21892            recovered.is_empty(),
21893            "the region declares nothing the file did not already declare: {recovered:?}"
21894        );
21895        let mut names: Vec<String> = parsed
21896            .declarations()
21897            .iter()
21898            .map(|unit| unit.fq_name())
21899            .collect();
21900        names.sort();
21901        assert_eq!(
21902            names,
21903            vec![
21904                "demo".to_string(),
21905                "demo.Widget".to_string(),
21906                "demo.Widget.doWork".to_string(),
21907            ]
21908        );
21909    }
21910
21911    /// Four macro-sentinel recoveries in one file (#941). Each one must record
21912    /// exactly the declarations it minted -- not the ones an earlier recovery
21913    /// minted, and not the file's other declarations -- in start-byte order,
21914    /// and each record must carry its own reparse window (#2787).
21915    #[test]
21916    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
21917        let mut source = String::new();
21918        for index in 0..4 {
21919            let _ = write!(
21920                source,
21921                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
21922            );
21923        }
21924        source.push_str("void outside() {}\n");
21925        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
21926
21927        let recovered: Vec<(String, (usize, usize))> = parsed
21928            .materialization_records
21929            .iter()
21930            .filter_map(|record| match record {
21931                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
21932                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
21933                }
21934                _ => None,
21935            })
21936            .collect();
21937
21938        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
21939        for index in 0..4 {
21940            // The window the reparse covers: everything after the opening
21941            // sentinel token up to the newline before the closing one.
21942            let region = format!("namespace demo{index}");
21943            let region_start = source.find(&region).expect("each region is in the source");
21944            let start = source[..region_start]
21945                .rfind("BEGIN_NS")
21946                .expect("each region opens with a sentinel")
21947                + "BEGIN_NS".len();
21948            let end = start
21949                + source[start..]
21950                    .find("END_NS")
21951                    .expect("each region closes with a sentinel")
21952                - 1;
21953            let window = (start, end);
21954            for name in [
21955                format!("demo{index}"),
21956                format!("demo{index}.Widget{index}"),
21957                format!("demo{index}.Widget{index}.doWork{index}"),
21958            ] {
21959                expected.push((name, window));
21960            }
21961        }
21962        assert_eq!(
21963            recovered, expected,
21964            "each recovery records its own minted declarations, in order"
21965        );
21966        assert!(
21967            parsed
21968                .declarations()
21969                .iter()
21970                .any(|unit| unit.fq_name() == "outside"),
21971            "the declaration outside every region stays parsed and unrecovered"
21972        );
21973    }
21974
21975    #[test]
21976    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
21977        assert!(
21978            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
21979            "the fixture must actually own fields"
21980        );
21981
21982        // The shapes that make the two implementations diverge if the index
21983        // records the wrong keys: a nested enum's owner is its whole dotted
21984        // chain, a class named like an enum owns fields under that same name, a
21985        // `$` in a short name is an owner separator the dotted prefix rule must
21986        // not split on, and the same enum name in two namespaces is two owners.
21987        for (source, name) in [
21988            ("struct S { enum E { V }; };\n", "nested.hpp"),
21989            (
21990                "enum Color { RED };\nstruct Color { int RED; };\n",
21991                "class-like.c",
21992            ),
21993            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
21994            (
21995                "enum E { V };\nnamespace ns { enum E { V }; }\n",
21996                "repeated.hpp",
21997            ),
21998            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
21999        ] {
22000            field_owner_index_agreement(source, name);
22001        }
22002    }
22003
22004    /// `blocks` copies of one plain class-with-methods template. The class the
22005    /// question is about is always the first one, so the only thing that
22006    /// changes between two of these sources is how much unrelated tree
22007    /// surrounds it.
22008    fn repeated_class_blocks(blocks: usize) -> String {
22009        let mut source = String::from("namespace demo {\n");
22010        for index in 0..blocks {
22011            let _ = write!(
22012                source,
22013                "\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"
22014            );
22015        }
22016        source.push_str("\n}\n");
22017        source
22018    }
22019
22020    /// #1496: asking whether a recovered class shape claims one range must cost
22021    /// the path to that range, not a pass over the whole translation unit.
22022    ///
22023    /// The C++ inverse scan asks this once per candidate type reference, so a
22024    /// whole-tree walk makes one file's scan quadratic in its own size. Both
22025    /// sources here answer `None` -- these are plain classes that no recovery
22026    /// shape claims -- which is exactly the case that used to pay full price.
22027    #[test]
22028    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22029        let mut answers = Vec::new();
22030        let mut visits = Vec::new();
22031        let mut node_counts = Vec::new();
22032        for blocks in [200usize, 400] {
22033            let source = repeated_class_blocks(blocks);
22034            let mut parser = tree_sitter::Parser::new();
22035            parser
22036                .set_language(&tree_sitter_cpp::LANGUAGE.into())
22037                .unwrap();
22038            let tree = parser.parse(&source, None).unwrap();
22039            let start_byte = source.find("class Widget0 ").expect("first class");
22040            let end_byte = start_byte
22041                + source[start_byte..]
22042                    .find("};")
22043                    .expect("first class terminator")
22044                + "};".len();
22045            let range = Range {
22046                start_byte,
22047                end_byte,
22048                start_line: 0,
22049                end_line: 0,
22050            };
22051            reset_recovered_class_body_node_visits_for_test();
22052            let recovered_export_classes =
22053                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22054            answers.push(recovered_class_body_at(
22055                &recovered_export_classes,
22056                tree.root_node(),
22057                &source,
22058                "Widget0",
22059                &range,
22060            ));
22061            visits.push(recovered_class_body_node_visits_for_test());
22062            let mut nodes = 0usize;
22063            let mut stack = vec![tree.root_node()];
22064            while let Some(node) = stack.pop() {
22065                nodes += 1;
22066                let mut cursor = node.walk();
22067                stack.extend(node.named_children(&mut cursor));
22068            }
22069            node_counts.push(nodes);
22070        }
22071
22072        assert_eq!(
22073            answers,
22074            vec![None, None],
22075            "no recovered shape claims a plain class"
22076        );
22077        assert_eq!(
22078            visits[0], visits[1],
22079            "the walk must follow the range's own path, so doubling the unrelated \
22080             classes must not change the node count: {visits:?} over trees of \
22081             {node_counts:?} nodes"
22082        );
22083        assert!(
22084            visits[1] * 20 < node_counts[1],
22085            "the walk must stay far below one pass over the tree: {visits:?} over \
22086             trees of {node_counts:?} nodes"
22087        );
22088    }
22089
22090    #[test]
22091    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22092        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22093        let mut parser = tree_sitter::Parser::new();
22094        parser
22095            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22096            .expect("C++ grammar");
22097        let tree = parser.parse(source, None).expect("fixture tree");
22098        let mut stack = vec![tree.root_node()];
22099        let mut recovered = None;
22100        while let Some(node) = stack.pop() {
22101            if node.kind() == "field_declaration"
22102                && let Some(field) = recovered_function_like_field_declarator(node, source)
22103            {
22104                recovered = Some((node, field.name));
22105                break;
22106            }
22107            let mut cursor = node.walk();
22108            stack.extend(node.named_children(&mut cursor));
22109        }
22110        let (declaration, name) = recovered.unwrap_or_else(|| {
22111            panic!(
22112                "pointer-wrapped macro field was not recovered: {}",
22113                tree.root_node().to_sexp()
22114            )
22115        });
22116        assert_eq!(node_text(name, source), "handshake");
22117        let recovered =
22118            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22119        assert_eq!(recovered.pointer_depth(), 1);
22120        assert_eq!(
22121            render_cpp_field_signature(declaration, name, source),
22122            "struct handshake * handshake;"
22123        );
22124    }
22125
22126    #[test]
22127    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22128        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22129        let mut parser = tree_sitter::Parser::new();
22130        parser
22131            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22132            .expect("C++ grammar");
22133        let tree = parser.parse(source, None).expect("fixture tree");
22134        let mut stack = vec![tree.root_node()];
22135        let mut recovered = None;
22136        while let Some(node) = stack.pop() {
22137            if let Some(field) = recovered_pyobject_head_field(node, source) {
22138                recovered = Some((node, field));
22139                break;
22140            }
22141            let mut cursor = node.walk();
22142            stack.extend(node.named_children(&mut cursor));
22143        }
22144        let (declaration, recovered) = recovered.unwrap_or_else(|| {
22145            panic!(
22146                "pointer field after PyObject_HEAD was not recovered: {}",
22147                tree.root_node().to_sexp()
22148            )
22149        });
22150        assert_eq!(node_text(recovered.name, source), "image");
22151        assert_eq!(recovered.pointer_depth(), 1);
22152        assert_eq!(
22153            render_cpp_field_signature(declaration, recovered.declarator, source),
22154            "ImagingObject *image;"
22155        );
22156    }
22157}